commit
71dc1d3395
|
@ -113,7 +113,8 @@ namespace MediaBrowser.Api
|
||||||
config.EnableCustomPathSubFolders = true;
|
config.EnableCustomPathSubFolders = true;
|
||||||
config.EnableStandaloneMusicKeys = true;
|
config.EnableStandaloneMusicKeys = true;
|
||||||
config.EnableCaseSensitiveItemIds = true;
|
config.EnableCaseSensitiveItemIds = true;
|
||||||
config.SchemaVersion = 87;
|
config.EnableFolderView = true;
|
||||||
|
config.SchemaVersion = 89;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Post(UpdateStartupConfiguration request)
|
public void Post(UpdateStartupConfiguration request)
|
||||||
|
|
|
@ -107,9 +107,11 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (EnablePooling())
|
var userdatakeys = GetUserDataKeys();
|
||||||
|
|
||||||
|
if (userdatakeys.Count > 1)
|
||||||
{
|
{
|
||||||
return GetUserDataKeys().First();
|
return userdatakeys[0];
|
||||||
}
|
}
|
||||||
return base.PresentationUniqueKey;
|
return base.PresentationUniqueKey;
|
||||||
}
|
}
|
||||||
|
@ -207,33 +209,13 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
{
|
{
|
||||||
IEnumerable<Season> seasons;
|
IEnumerable<Season> seasons;
|
||||||
|
|
||||||
if (EnablePooling())
|
|
||||||
{
|
|
||||||
var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
|
|
||||||
{
|
|
||||||
PresentationUniqueKey = PresentationUniqueKey,
|
|
||||||
IncludeItemTypes = new[] { typeof(Series).Name }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (seriesIds.Count > 1)
|
|
||||||
{
|
|
||||||
seasons = LibraryManager.GetItemList(new InternalItemsQuery(user)
|
seasons = LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(),
|
AncestorWithPresentationUniqueKey = PresentationUniqueKey,
|
||||||
IncludeItemTypes = new[] { typeof(Season).Name },
|
IncludeItemTypes = new[] { typeof(Season).Name },
|
||||||
SortBy = new[] { ItemSortBy.SortName }
|
SortBy = new[] { ItemSortBy.SortName }
|
||||||
|
|
||||||
}).Cast<Season>();
|
}).Cast<Season>();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
seasons = LibraryManager.Sort(base.GetChildren(user, true), user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).OfType<Season>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
seasons = LibraryManager.Sort(base.GetChildren(user, true), user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).OfType<Season>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!includeMissingSeasons)
|
if (!includeMissingSeasons)
|
||||||
{
|
{
|
||||||
|
@ -256,9 +238,16 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
|
|
||||||
public IEnumerable<Episode> GetEpisodes(User user, bool includeMissing, bool includeVirtualUnaired)
|
public IEnumerable<Episode> GetEpisodes(User user, bool includeMissing, bool includeVirtualUnaired)
|
||||||
{
|
{
|
||||||
var allSeriesEpisodes = GetAllEpisodes(user).ToList();
|
var allItems = LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||||
|
{
|
||||||
|
AncestorWithPresentationUniqueKey = PresentationUniqueKey,
|
||||||
|
IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name },
|
||||||
|
SortBy = new[] { ItemSortBy.SortName }
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
var allEpisodes = GetSeasons(user, true, true)
|
var allSeriesEpisodes = allItems.OfType<Episode>().ToList();
|
||||||
|
|
||||||
|
var allEpisodes = allItems.OfType<Season>()
|
||||||
.SelectMany(i => i.GetEpisodes(this, user, includeMissing, includeVirtualUnaired, allSeriesEpisodes))
|
.SelectMany(i => i.GetEpisodes(this, user, includeMissing, includeVirtualUnaired, allSeriesEpisodes))
|
||||||
.Reverse()
|
.Reverse()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
@ -343,51 +332,16 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
return GetEpisodes(user, season, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
|
return GetEpisodes(user, season, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool EnablePooling()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<Episode> GetAllEpisodes(User user)
|
private IEnumerable<Episode> GetAllEpisodes(User user)
|
||||||
{
|
{
|
||||||
IEnumerable<Episode> episodes;
|
return LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||||
|
|
||||||
if (EnablePooling())
|
|
||||||
{
|
{
|
||||||
var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
|
AncestorWithPresentationUniqueKey = PresentationUniqueKey,
|
||||||
{
|
|
||||||
PresentationUniqueKey = PresentationUniqueKey,
|
|
||||||
IncludeItemTypes = new[] { typeof(Series).Name }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (seriesIds.Count > 1)
|
|
||||||
{
|
|
||||||
episodes = LibraryManager.GetItemList(new InternalItemsQuery(user)
|
|
||||||
{
|
|
||||||
AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(),
|
|
||||||
IncludeItemTypes = new[] { typeof(Episode).Name },
|
IncludeItemTypes = new[] { typeof(Episode).Name },
|
||||||
SortBy = new[] { ItemSortBy.SortName }
|
SortBy = new[] { ItemSortBy.SortName }
|
||||||
|
|
||||||
}).Cast<Episode>();
|
}).Cast<Episode>();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
episodes = GetRecursiveChildren(user, new InternalItemsQuery(user)
|
|
||||||
{
|
|
||||||
IncludeItemTypes = new[] { typeof(Episode).Name }
|
|
||||||
}).Cast<Episode>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
episodes = GetRecursiveChildren(user, new InternalItemsQuery(user)
|
|
||||||
{
|
|
||||||
IncludeItemTypes = new[] { typeof(Episode).Name }
|
|
||||||
}).Cast<Episode>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return episodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Episode> GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
|
public IEnumerable<Episode> GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
|
||||||
{
|
{
|
||||||
|
|
|
@ -198,6 +198,7 @@ namespace MediaBrowser.Model.Configuration
|
||||||
public bool EnableAnonymousUsageReporting { get; set; }
|
public bool EnableAnonymousUsageReporting { get; set; }
|
||||||
public bool EnableStandaloneMusicKeys { get; set; }
|
public bool EnableStandaloneMusicKeys { get; set; }
|
||||||
public bool EnableLocalizedGuids { get; set; }
|
public bool EnableLocalizedGuids { get; set; }
|
||||||
|
public bool EnableFolderView { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using MediaBrowser.Common.Net;
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
|
@ -6,7 +8,10 @@ using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Providers;
|
using MediaBrowser.Model.Providers;
|
||||||
|
using MediaBrowser.Model.Serialization;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -15,10 +20,16 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
public class OmdbImageProvider : IRemoteImageProvider, IHasOrder
|
public class OmdbImageProvider : IRemoteImageProvider, IHasOrder
|
||||||
{
|
{
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly IJsonSerializer _jsonSerializer;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
private readonly IServerConfigurationManager _configurationManager;
|
||||||
|
|
||||||
public OmdbImageProvider(IHttpClient httpClient)
|
public OmdbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
|
||||||
{
|
{
|
||||||
|
_jsonSerializer = jsonSerializer;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
_configurationManager = configurationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
|
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
|
||||||
|
@ -29,13 +40,19 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
|
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var imdbId = item.GetProviderId(MetadataProviders.Imdb);
|
var imdbId = item.GetProviderId(MetadataProviders.Imdb);
|
||||||
|
|
||||||
var list = new List<RemoteImageInfo>();
|
var list = new List<RemoteImageInfo>();
|
||||||
|
|
||||||
|
var provider = new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(imdbId))
|
if (!string.IsNullOrWhiteSpace(imdbId))
|
||||||
|
{
|
||||||
|
OmdbProvider.RootObject rootObject = await provider.GetRootObject(imdbId, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(rootObject.Poster))
|
||||||
{
|
{
|
||||||
list.Add(new RemoteImageInfo
|
list.Add(new RemoteImageInfo
|
||||||
{
|
{
|
||||||
|
@ -43,8 +60,9 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId)
|
Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
|
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
|
||||||
|
@ -65,18 +83,6 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
|
|
||||||
public bool Supports(IHasImages item)
|
public bool Supports(IHasImages item)
|
||||||
{
|
{
|
||||||
// We'll hammer Omdb if we enable this
|
|
||||||
if (item is Person)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the http requests since we know it's not currently supported
|
|
||||||
if (item is Season || item is Episode)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Supports images for tv movies
|
// Supports images for tv movies
|
||||||
var tvProgram = item as LiveTvProgram;
|
var tvProgram = item as LiveTvProgram;
|
||||||
if (tvProgram != null && tvProgram.IsMovie)
|
if (tvProgram != null && tvProgram.IsMovie)
|
||||||
|
@ -84,7 +90,7 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return item is Movie || item is Trailer;
|
return item is Movie || item is Trailer || item is Episode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Order
|
public int Order
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using MediaBrowser.Common.Net;
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
|
@ -26,13 +28,17 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
private readonly IServerConfigurationManager _configurationManager;
|
||||||
|
|
||||||
public OmdbItemProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager)
|
public OmdbItemProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
|
||||||
{
|
{
|
||||||
_jsonSerializer = jsonSerializer;
|
_jsonSerializer = jsonSerializer;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
_configurationManager = configurationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
|
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
|
||||||
|
@ -220,7 +226,7 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
||||||
result.HasMetadata = true;
|
result.HasMetadata = true;
|
||||||
|
|
||||||
await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -259,7 +265,7 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
||||||
result.HasMetadata = true;
|
result.HasMetadata = true;
|
||||||
|
|
||||||
await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
using MediaBrowser.Common.Net;
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
|
@ -17,17 +20,17 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
{
|
{
|
||||||
internal static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
|
internal static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
|
||||||
private readonly IJsonSerializer _jsonSerializer;
|
private readonly IJsonSerializer _jsonSerializer;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
private readonly IServerConfigurationManager _configurationManager;
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||||
|
|
||||||
public static OmdbProvider Current;
|
public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
|
||||||
|
|
||||||
public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient)
|
|
||||||
{
|
{
|
||||||
_jsonSerializer = jsonSerializer;
|
_jsonSerializer = jsonSerializer;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
Current = this;
|
_configurationManager = configurationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Fetch(BaseItem item, string imdbId, string language, string country, CancellationToken cancellationToken)
|
public async Task Fetch(BaseItem item, string imdbId, string language, string country, CancellationToken cancellationToken)
|
||||||
|
@ -37,28 +40,7 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
throw new ArgumentNullException("imdbId");
|
throw new ArgumentNullException("imdbId");
|
||||||
}
|
}
|
||||||
|
|
||||||
var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId;
|
var result = await GetRootObject(imdbId, cancellationToken);
|
||||||
|
|
||||||
var url = string.Format("https://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam);
|
|
||||||
|
|
||||||
using (var stream = await _httpClient.Get(new HttpRequestOptions
|
|
||||||
{
|
|
||||||
Url = url,
|
|
||||||
ResourcePool = ResourcePool,
|
|
||||||
CancellationToken = cancellationToken
|
|
||||||
|
|
||||||
}).ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
string resultString;
|
|
||||||
|
|
||||||
using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
|
|
||||||
{
|
|
||||||
resultString = reader.ReadToEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
resultString = resultString.Replace("\"N/A\"", "\"\"");
|
|
||||||
|
|
||||||
var result = _jsonSerializer.DeserializeFromString<RootObject>(resultString);
|
|
||||||
|
|
||||||
// Only take the name and rating if the user's language is set to english, since Omdb has no localization
|
// Only take the name and rating if the user's language is set to english, since Omdb has no localization
|
||||||
if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
|
||||||
|
@ -131,6 +113,78 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
|
|
||||||
ParseAdditionalMetadata(item, result);
|
ParseAdditionalMetadata(item, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task<RootObject> GetRootObject(string imdbId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var path = await EnsureItemInfo(imdbId, cancellationToken);
|
||||||
|
|
||||||
|
string resultString;
|
||||||
|
|
||||||
|
using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072))
|
||||||
|
{
|
||||||
|
using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
|
||||||
|
{
|
||||||
|
resultString = reader.ReadToEnd();
|
||||||
|
resultString = resultString.Replace("\"N/A\"", "\"\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _jsonSerializer.DeserializeFromString<RootObject>(resultString);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(imdbId))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("imdbId");
|
||||||
|
}
|
||||||
|
|
||||||
|
var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId;
|
||||||
|
|
||||||
|
var path = GetDataFilePath(imdbParam);
|
||||||
|
|
||||||
|
var fileInfo = _fileSystem.GetFileSystemInfo(path);
|
||||||
|
|
||||||
|
if (fileInfo.Exists)
|
||||||
|
{
|
||||||
|
// If it's recent or automatic updates are enabled, don't re-download
|
||||||
|
if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = string.Format("https://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam);
|
||||||
|
|
||||||
|
using (var stream = await _httpClient.Get(new HttpRequestOptions
|
||||||
|
{
|
||||||
|
Url = url,
|
||||||
|
ResourcePool = ResourcePool,
|
||||||
|
CancellationToken = cancellationToken
|
||||||
|
|
||||||
|
}).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
var rootObject = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
|
||||||
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
_jsonSerializer.SerializeToFile(rootObject, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string GetDataFilePath(string imdbId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(imdbId))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("imdbId");
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb");
|
||||||
|
|
||||||
|
var filename = string.Format("{0}.json", imdbId);
|
||||||
|
|
||||||
|
return Path.Combine(dataPath, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParseAdditionalMetadata(BaseItem item, RootObject result)
|
private void ParseAdditionalMetadata(BaseItem item, RootObject result)
|
||||||
|
@ -184,7 +238,7 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase);
|
return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RootObject
|
internal class RootObject
|
||||||
{
|
{
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Year { get; set; }
|
public string Year { get; set; }
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using MediaBrowser.Common.Net;
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
|
@ -21,12 +23,16 @@ namespace MediaBrowser.Providers.TV
|
||||||
private readonly IJsonSerializer _jsonSerializer;
|
private readonly IJsonSerializer _jsonSerializer;
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
private readonly OmdbItemProvider _itemProvider;
|
private readonly OmdbItemProvider _itemProvider;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
private readonly IServerConfigurationManager _configurationManager;
|
||||||
|
|
||||||
public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager)
|
public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
|
||||||
{
|
{
|
||||||
_jsonSerializer = jsonSerializer;
|
_jsonSerializer = jsonSerializer;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_itemProvider = new OmdbItemProvider(jsonSerializer, httpClient, logger, libraryManager);
|
_fileSystem = fileSystem;
|
||||||
|
_configurationManager = configurationManager;
|
||||||
|
_itemProvider = new OmdbItemProvider(jsonSerializer, httpClient, logger, libraryManager, fileSystem, configurationManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
|
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
|
||||||
|
@ -58,7 +64,7 @@ namespace MediaBrowser.Providers.TV
|
||||||
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
||||||
result.HasMetadata = true;
|
result.HasMetadata = true;
|
||||||
|
|
||||||
await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -114,6 +114,22 @@ namespace MediaBrowser.Providers.TV
|
||||||
item.CommunityRating = (float)response.vote_average;
|
item.CommunityRating = (float)response.vote_average;
|
||||||
item.VoteCount = response.vote_count;
|
item.VoteCount = response.vote_count;
|
||||||
|
|
||||||
|
if (response.videos != null && response.videos.results != null)
|
||||||
|
{
|
||||||
|
foreach (var video in response.videos.results)
|
||||||
|
{
|
||||||
|
if (video.type.Equals("trailer", System.StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| video.type.Equals("clip", System.StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (video.site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.key);
|
||||||
|
item.AddTrailerUrl(videoUrl, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result.ResetPeople();
|
result.ResetPeople();
|
||||||
|
|
||||||
var credits = response.credits;
|
var credits = response.credits;
|
||||||
|
|
|
@ -210,7 +210,19 @@ namespace MediaBrowser.Providers.TV
|
||||||
|
|
||||||
public class Videos
|
public class Videos
|
||||||
{
|
{
|
||||||
public List<object> results { get; set; }
|
public List<Video> results { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Video
|
||||||
|
{
|
||||||
|
public string id { get; set; }
|
||||||
|
public string iso_639_1 { get; set; }
|
||||||
|
public string iso_3166_1 { get; set; }
|
||||||
|
public string key { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public string site { get; set; }
|
||||||
|
public string size { get; set; }
|
||||||
|
public string type { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RootObject
|
public class RootObject
|
||||||
|
|
|
@ -58,7 +58,7 @@ namespace MediaBrowser.Providers.TV
|
||||||
|
|
||||||
result.HasMetadata = true;
|
result.HasMetadata = true;
|
||||||
result.Item = new Season();
|
result.Item = new Season();
|
||||||
result.Item.Name = info.Name;
|
result.Item.Name = seasonInfo.name;
|
||||||
result.Item.IndexNumber = seasonNumber;
|
result.Item.IndexNumber = seasonNumber;
|
||||||
|
|
||||||
result.Item.Overview = seasonInfo.overview;
|
result.Item.Overview = seasonInfo.overview;
|
||||||
|
|
|
@ -168,7 +168,7 @@ namespace MediaBrowser.Providers.TV
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
result.Item = await FetchMovieData(tmdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
result.Item = await FetchSeriesData(tmdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
result.HasMetadata = result.Item != null;
|
result.HasMetadata = result.Item != null;
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ namespace MediaBrowser.Providers.TV
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Series> FetchMovieData(string tmdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
|
private async Task<Series> FetchSeriesData(string tmdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
string dataFilePath = null;
|
string dataFilePath = null;
|
||||||
RootObject seriesInfo = null;
|
RootObject seriesInfo = null;
|
||||||
|
@ -285,6 +285,21 @@ namespace MediaBrowser.Providers.TV
|
||||||
series.OfficialRating = minimumRelease.rating;
|
series.OfficialRating = minimumRelease.rating;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (seriesInfo.videos != null && seriesInfo.videos.results != null)
|
||||||
|
{
|
||||||
|
foreach (var video in seriesInfo.videos.results)
|
||||||
|
{
|
||||||
|
if (video.type.Equals("trailer", System.StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| video.type.Equals("clip", System.StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (video.site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.key);
|
||||||
|
series.AddTrailerUrl(videoUrl, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string GetSeriesDataPath(IApplicationPaths appPaths, string tmdbId)
|
internal static string GetSeriesDataPath(IApplicationPaths appPaths, string tmdbId)
|
||||||
|
@ -555,7 +570,19 @@ namespace MediaBrowser.Providers.TV
|
||||||
|
|
||||||
public class Videos
|
public class Videos
|
||||||
{
|
{
|
||||||
public List<object> results { get; set; }
|
public List<Video> results { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Video
|
||||||
|
{
|
||||||
|
public string id { get; set; }
|
||||||
|
public string iso_639_1 { get; set; }
|
||||||
|
public string iso_3166_1 { get; set; }
|
||||||
|
public string key { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public string site { get; set; }
|
||||||
|
public string size { get; set; }
|
||||||
|
public string type { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContentRating
|
public class ContentRating
|
||||||
|
|
|
@ -105,6 +105,12 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_config.Configuration.EnableFolderView)
|
||||||
|
{
|
||||||
|
var name = _localizationManager.GetLocalizedString("ViewType" + CollectionType.Folders);
|
||||||
|
list.Add(await _libraryManager.GetNamedView(name, CollectionType.Folders, string.Empty, cancellationToken).ConfigureAwait(false));
|
||||||
|
}
|
||||||
|
|
||||||
if (query.IncludeExternalContent)
|
if (query.IncludeExternalContent)
|
||||||
{
|
{
|
||||||
var channelResult = await _channelManager.GetChannelsInternal(new ChannelQuery
|
var channelResult = await _channelManager.GetChannelsInternal(new ChannelQuery
|
||||||
|
|
|
@ -94,7 +94,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
private IDbCommand _updateInheritedRatingCommand;
|
private IDbCommand _updateInheritedRatingCommand;
|
||||||
private IDbCommand _updateInheritedTagsCommand;
|
private IDbCommand _updateInheritedTagsCommand;
|
||||||
|
|
||||||
public const int LatestSchemaVersion = 87;
|
public const int LatestSchemaVersion = 89;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
|
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
|
||||||
|
@ -137,6 +137,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
"create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID, Path TEXT)",
|
"create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID, Path TEXT)",
|
||||||
"create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
|
"create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
|
||||||
"create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
|
"create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
|
||||||
|
"create index if not exists idx_TypedBaseItems2 on TypedBaseItems(Type,Guid)",
|
||||||
|
|
||||||
"create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))",
|
"create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))",
|
||||||
"create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",
|
"create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",
|
||||||
|
|
|
@ -125,7 +125,7 @@ namespace MediaBrowser.Server.Implementations.TV
|
||||||
private Tuple<Episode, DateTime, bool> GetNextUp(Series series, User user)
|
private Tuple<Episode, DateTime, bool> GetNextUp(Series series, User user)
|
||||||
{
|
{
|
||||||
// Get them in display order, then reverse
|
// Get them in display order, then reverse
|
||||||
var allEpisodes = series.GetEpisodes(user, true, true)
|
var allEpisodes = series.GetEpisodes(user, false, false)
|
||||||
.Where(i => !i.ParentIndexNumber.HasValue || i.ParentIndexNumber.Value != 0)
|
.Where(i => !i.ParentIndexNumber.HasValue || i.ParentIndexNumber.Value != 0)
|
||||||
.Reverse()
|
.Reverse()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
@ -134,8 +134,6 @@ namespace MediaBrowser.Server.Implementations.TV
|
||||||
var lastWatchedDate = DateTime.MinValue;
|
var lastWatchedDate = DateTime.MinValue;
|
||||||
Episode nextUp = null;
|
Episode nextUp = null;
|
||||||
|
|
||||||
var includeMissing = user.Configuration.DisplayMissingEpisodes;
|
|
||||||
|
|
||||||
var unplayedEpisodes = new List<Episode>();
|
var unplayedEpisodes = new List<Episode>();
|
||||||
|
|
||||||
// Go back starting with the most recent episodes
|
// Go back starting with the most recent episodes
|
||||||
|
@ -157,12 +155,9 @@ namespace MediaBrowser.Server.Implementations.TV
|
||||||
{
|
{
|
||||||
unplayedEpisodes.Add(episode);
|
unplayedEpisodes.Add(episode);
|
||||||
|
|
||||||
if (!episode.IsVirtualUnaired && (includeMissing || !episode.IsMissingEpisode))
|
|
||||||
{
|
|
||||||
nextUp = episode;
|
nextUp = episode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (lastWatched != null)
|
if (lastWatched != null)
|
||||||
{
|
{
|
||||||
|
@ -175,12 +170,9 @@ namespace MediaBrowser.Server.Implementations.TV
|
||||||
{
|
{
|
||||||
var unplayedEpisode = unplayedEpisodes[i];
|
var unplayedEpisode = unplayedEpisodes[i];
|
||||||
|
|
||||||
if (!unplayedEpisode.IsVirtualUnaired && (includeMissing || !unplayedEpisode.IsMissingEpisode))
|
|
||||||
{
|
|
||||||
firstEpisode = unplayedEpisode;
|
firstEpisode = unplayedEpisode;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Return the first episode
|
// Return the first episode
|
||||||
return new Tuple<Episode, DateTime, bool>(firstEpisode, DateTime.MinValue, true);
|
return new Tuple<Episode, DateTime, bool>(firstEpisode, DateTime.MinValue, true);
|
||||||
|
|
|
@ -380,7 +380,8 @@ namespace MediaBrowser.Server.Startup.Common
|
||||||
{
|
{
|
||||||
new OmdbEpisodeProviderMigration(ServerConfigurationManager),
|
new OmdbEpisodeProviderMigration(ServerConfigurationManager),
|
||||||
new MovieDbEpisodeProviderMigration(ServerConfigurationManager),
|
new MovieDbEpisodeProviderMigration(ServerConfigurationManager),
|
||||||
new DbMigration(ServerConfigurationManager, TaskManager)
|
new DbMigration(ServerConfigurationManager, TaskManager),
|
||||||
|
new FolderViewSettingMigration(ServerConfigurationManager, UserManager)
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var task in migrations)
|
foreach (var task in migrations)
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
<Compile Include="FFMpeg\FFmpegValidator.cs" />
|
<Compile Include="FFMpeg\FFmpegValidator.cs" />
|
||||||
<Compile Include="INativeApp.cs" />
|
<Compile Include="INativeApp.cs" />
|
||||||
<Compile Include="MbLinkShortcutHandler.cs" />
|
<Compile Include="MbLinkShortcutHandler.cs" />
|
||||||
|
<Compile Include="Migrations\FolderViewSettingMigration.cs" />
|
||||||
<Compile Include="Migrations\IVersionMigration.cs" />
|
<Compile Include="Migrations\IVersionMigration.cs" />
|
||||||
<Compile Include="Migrations\DbMigration.cs" />
|
<Compile Include="Migrations\DbMigration.cs" />
|
||||||
<Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" />
|
<Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" />
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
using System.Linq;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Server.Startup.Common.Migrations
|
||||||
|
{
|
||||||
|
public class FolderViewSettingMigration : IVersionMigration
|
||||||
|
{
|
||||||
|
private readonly IServerConfigurationManager _config;
|
||||||
|
private readonly IUserManager _userManager;
|
||||||
|
|
||||||
|
public FolderViewSettingMigration(IServerConfigurationManager config, IUserManager userManager)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
_userManager = userManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
var migrationKey = this.GetType().Name;
|
||||||
|
var migrationKeyList = _config.Configuration.Migrations.ToList();
|
||||||
|
|
||||||
|
if (!migrationKeyList.Contains(migrationKey))
|
||||||
|
{
|
||||||
|
if (_config.Configuration.IsStartupWizardCompleted)
|
||||||
|
{
|
||||||
|
_config.Configuration.EnableFolderView = _userManager.Users.Any(i => i.Configuration.DisplayFoldersView);
|
||||||
|
}
|
||||||
|
|
||||||
|
migrationKeyList.Add(migrationKey);
|
||||||
|
_config.Configuration.Migrations = migrationKeyList.ToArray();
|
||||||
|
_config.SaveConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -272,6 +272,9 @@
|
||||||
<Content Include="dashboard-ui\legacy\selectmenu.js">
|
<Content Include="dashboard-ui\legacy\selectmenu.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\librarydisplay.html">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\livetvguideprovider.html">
|
<Content Include="dashboard-ui\livetvguideprovider.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -308,6 +311,9 @@
|
||||||
<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\librarydisplay.js">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\scripts\livetvguideprovider.js">
|
<Content Include="dashboard-ui\scripts\livetvguideprovider.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user