diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 972578b7d..fead8e8a8 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -82,7 +82,7 @@
-
+
diff --git a/MediaBrowser.Providers/Music/MusicAlbumDynamicInfoProvider.cs b/MediaBrowser.Providers/Music/AlbumDynamicInfoProvider.cs
similarity index 93%
rename from MediaBrowser.Providers/Music/MusicAlbumDynamicInfoProvider.cs
rename to MediaBrowser.Providers/Music/AlbumDynamicInfoProvider.cs
index 521d29d89..085ff9b54 100644
--- a/MediaBrowser.Providers/Music/MusicAlbumDynamicInfoProvider.cs
+++ b/MediaBrowser.Providers/Music/AlbumDynamicInfoProvider.cs
@@ -13,14 +13,14 @@ namespace MediaBrowser.Providers.Music
///
/// Class MusicAlbumDynamicInfoProvider
///
- public class MusicAlbumDynamicInfoProvider : BaseMetadataProvider, IDynamicInfoProvider
+ public class AlbumDynamicInfoProvider : BaseMetadataProvider, IDynamicInfoProvider
{
///
/// Initializes a new instance of the class.
///
/// The log manager.
/// The configuration manager.
- public MusicAlbumDynamicInfoProvider(ILogManager logManager, IServerConfigurationManager configurationManager)
+ public AlbumDynamicInfoProvider(ILogManager logManager, IServerConfigurationManager configurationManager)
: base(logManager, configurationManager)
{
}
diff --git a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs
index ea4ef5e21..be7ff192e 100644
--- a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Net;
+using System.Collections.Generic;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
@@ -127,7 +128,7 @@ namespace MediaBrowser.Providers.TV
return base.NeedsRefreshInternal(item, providerInfo);
}
- protected override DateTime CompareDate(BaseItem item)
+ protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
{
var episode = (Episode)item;
@@ -136,17 +137,90 @@ namespace MediaBrowser.Providers.TV
if (!string.IsNullOrEmpty(seriesId))
{
// Process images
- var seriesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower() + ".xml");
+ var seriesDataPath = RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId);
- var seriesXmlFileInfo = new FileInfo(seriesXmlPath);
+ var files = GetEpisodeXmlFiles(episode, seriesDataPath);
- if (seriesXmlFileInfo.Exists)
+ if (files.Count > 0)
{
- return seriesXmlFileInfo.LastWriteTimeUtc;
+ return files.Select(i => i.LastWriteTimeUtc).Max() > providerInfo.LastRefreshed;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Gets the episode XML files.
+ ///
+ /// The episode.
+ /// The series data path.
+ /// List{FileInfo}.
+ private List GetEpisodeXmlFiles(Episode episode, string seriesDataPath)
+ {
+ var files = new List();
+
+ if (episode.IndexNumber == null)
+ {
+ return files;
+ }
+
+ var episodeNumber = episode.IndexNumber.Value;
+ var seasonNumber = episode.ParentIndexNumber;
+
+ if (seasonNumber == null)
+ {
+ return files;
+ }
+
+ var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber));
+
+ var fileInfo = new FileInfo(file);
+ var usingAbsoluteData = false;
+
+ if (fileInfo.Exists)
+ {
+ files.Add(fileInfo);
+ }
+ else
+ {
+ file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber));
+ fileInfo = new FileInfo(file);
+ if (fileInfo.Exists)
+ {
+ files.Add(fileInfo);
+ usingAbsoluteData = true;
}
}
- return base.CompareDate(item);
+ var end = episode.IndexNumberEnd ?? episodeNumber;
+ episodeNumber++;
+
+ while (episodeNumber <= end)
+ {
+ if (usingAbsoluteData)
+ {
+ file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber));
+ }
+ else
+ {
+ file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber));
+ }
+
+ fileInfo = new FileInfo(file);
+ if (fileInfo.Exists)
+ {
+ files.Add(fileInfo);
+ }
+ else
+ {
+ break;
+ }
+
+ episodeNumber++;
+ }
+
+ return files;
}
///
@@ -213,7 +287,7 @@ namespace MediaBrowser.Providers.TV
}
var episodeNumber = episode.IndexNumber.Value;
- var seasonNumber = episode.ParentIndexNumber ?? TVUtils.GetSeasonNumberFromEpisodeFile(episode.Path);
+ var seasonNumber = episode.ParentIndexNumber;
if (seasonNumber == null)
{
diff --git a/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs b/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs
index e86e53e3b..161348935 100644
--- a/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs
@@ -252,7 +252,7 @@ namespace MediaBrowser.Providers.TV
// The prescan task will take care of updates so we don't need to re-download here
if (!files.Contains("banners.xml", StringComparer.OrdinalIgnoreCase) || !files.Contains("actors.xml", StringComparer.OrdinalIgnoreCase) || !files.Contains(seriesXmlFilename, StringComparer.OrdinalIgnoreCase))
{
- await DownloadSeriesZip(seriesId, seriesDataPath, cancellationToken).ConfigureAwait(false);
+ await DownloadSeriesZip(seriesId, seriesDataPath, null, cancellationToken).ConfigureAwait(false);
}
// Examine if there's no local metadata, or save local is on (to get updates)
@@ -279,7 +279,7 @@ namespace MediaBrowser.Providers.TV
/// The series data path.
/// The cancellation token.
/// Task.
- internal async Task DownloadSeriesZip(string seriesId, string seriesDataPath, CancellationToken cancellationToken)
+ internal async Task DownloadSeriesZip(string seriesId, string seriesDataPath, long? lastTvDbUpdateTime, CancellationToken cancellationToken)
{
var url = string.Format(SeriesGetZip, TVUtils.TvdbApiKey, seriesId, ConfigurationManager.Configuration.PreferredMetadataLanguage);
@@ -301,12 +301,14 @@ namespace MediaBrowser.Providers.TV
}
}
- foreach (var file in Directory.EnumerateFiles(seriesDataPath, "*.xml", SearchOption.AllDirectories).ToList())
+ // Sanitize all files, except for extracted episode files
+ foreach (var file in Directory.EnumerateFiles(seriesDataPath, "*.xml", SearchOption.AllDirectories).ToList()
+ .Where(i => !Path.GetFileName(i).StartsWith("episode-", StringComparison.OrdinalIgnoreCase)))
{
await SanitizeXmlFile(file).ConfigureAwait(false);
}
- await ExtractEpisodes(seriesDataPath, Path.Combine(seriesDataPath, ConfigurationManager.Configuration.PreferredMetadataLanguage + ".xml")).ConfigureAwait(false);
+ await ExtractEpisodes(seriesDataPath, Path.Combine(seriesDataPath, ConfigurationManager.Configuration.PreferredMetadataLanguage + ".xml"), lastTvDbUpdateTime).ConfigureAwait(false);
}
///
@@ -369,8 +371,9 @@ namespace MediaBrowser.Providers.TV
///
/// The series data path.
/// The XML file.
+ /// The last tv db update time.
/// Task.
- private async Task ExtractEpisodes(string seriesDataPath, string xmlFile)
+ private async Task ExtractEpisodes(string seriesDataPath, string xmlFile, long? lastTvDbUpdateTime)
{
var settings = new XmlReaderSettings
{
@@ -398,7 +401,7 @@ namespace MediaBrowser.Providers.TV
{
var outerXml = reader.ReadOuterXml();
- await SaveEpsiodeXml(seriesDataPath, outerXml).ConfigureAwait(false);
+ await SaveEpsiodeXml(seriesDataPath, outerXml, lastTvDbUpdateTime).ConfigureAwait(false);
break;
}
@@ -412,7 +415,7 @@ namespace MediaBrowser.Providers.TV
}
}
- private async Task SaveEpsiodeXml(string seriesDataPath, string xml)
+ private async Task SaveEpsiodeXml(string seriesDataPath, string xml, long? lastTvDbUpdateTime)
{
var settings = new XmlReaderSettings
{
@@ -425,6 +428,7 @@ namespace MediaBrowser.Providers.TV
var seasonNumber = -1;
var episodeNumber = -1;
var absoluteNumber = -1;
+ var lastUpdateString = string.Empty;
using (var streamReader = new StringReader(xml))
{
@@ -440,6 +444,12 @@ namespace MediaBrowser.Providers.TV
{
switch (reader.Name)
{
+ case "lastupdated":
+ {
+ lastUpdateString = reader.ReadElementContentAsString();
+ break;
+ }
+
case "EpisodeNumber":
{
var val = reader.ReadElementContentAsString();
@@ -491,21 +501,21 @@ namespace MediaBrowser.Providers.TV
}
}
- var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber, episodeNumber));
-
- using (var writer = XmlWriter.Create(file, new XmlWriterSettings
+ var hasEpisodeChanged = true;
+ if (!string.IsNullOrEmpty(lastUpdateString) && lastTvDbUpdateTime.HasValue)
{
- Encoding = Encoding.UTF8,
- Async = true
- }))
- {
- await writer.WriteRawAsync(xml).ConfigureAwait(false);
+ long num;
+ if (long.TryParse(lastUpdateString, NumberStyles.Any, UsCulture, out num))
+ {
+ hasEpisodeChanged = num >= lastTvDbUpdateTime.Value;
+ }
}
- if (absoluteNumber != -1)
- {
- file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", absoluteNumber));
+ var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber, episodeNumber));
+ // Only save the file if not already there, or if the episode has changed
+ if (hasEpisodeChanged || !File.Exists(file))
+ {
using (var writer = XmlWriter.Create(file, new XmlWriterSettings
{
Encoding = Encoding.UTF8,
@@ -515,6 +525,24 @@ namespace MediaBrowser.Providers.TV
await writer.WriteRawAsync(xml).ConfigureAwait(false);
}
}
+
+ if (absoluteNumber != -1)
+ {
+ file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", absoluteNumber));
+
+ // Only save the file if not already there, or if the episode has changed
+ if (hasEpisodeChanged || !File.Exists(file))
+ {
+ using (var writer = XmlWriter.Create(file, new XmlWriterSettings
+ {
+ Encoding = Encoding.UTF8,
+ Async = true
+ }))
+ {
+ await writer.WriteRawAsync(xml).ConfigureAwait(false);
+ }
+ }
+ }
}
///
diff --git a/MediaBrowser.Providers/TV/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TvdbPrescanTask.cs
index 0bc7cc3e7..94f857d9c 100644
--- a/MediaBrowser.Providers/TV/TvdbPrescanTask.cs
+++ b/MediaBrowser.Providers/TV/TvdbPrescanTask.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Net;
+using System.Globalization;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
@@ -55,6 +56,8 @@ namespace MediaBrowser.Providers.TV
_config = config;
}
+ protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
///
/// Runs the specified progress.
///
@@ -72,7 +75,7 @@ namespace MediaBrowser.Providers.TV
var path = RemoteSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths);
Directory.CreateDirectory(path);
-
+
var timestampFile = Path.Combine(path, "time.txt");
var timestampFileInfo = new FileInfo(timestampFile);
@@ -106,7 +109,7 @@ namespace MediaBrowser.Providers.TV
newUpdateTime = GetUpdateTime(stream);
}
- await UpdateSeries(existingDirectories, path, progress, cancellationToken).ConfigureAwait(false);
+ await UpdateSeries(existingDirectories, path, null, progress, cancellationToken).ConfigureAwait(false);
}
else
{
@@ -114,7 +117,13 @@ namespace MediaBrowser.Providers.TV
newUpdateTime = seriesToUpdate.Item2;
- await UpdateSeries(seriesToUpdate.Item1, path, progress, cancellationToken).ConfigureAwait(false);
+ long lastUpdateValue;
+
+ long.TryParse(lastUpdateTime, NumberStyles.Any, UsCulture, out lastUpdateValue);
+
+ var nullableUpdateValue = lastUpdateValue == 0 ? (long?)null : lastUpdateValue;
+
+ await UpdateSeries(seriesToUpdate.Item1, path, nullableUpdateValue, progress, cancellationToken).ConfigureAwait(false);
}
File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8);
@@ -251,10 +260,11 @@ namespace MediaBrowser.Providers.TV
///
/// The series ids.
/// The series data path.
+ /// The last tv db update time.
/// The progress.
/// The cancellation token.
/// Task.
- private async Task UpdateSeries(IEnumerable seriesIds, string seriesDataPath, IProgress progress, CancellationToken cancellationToken)
+ private async Task UpdateSeries(IEnumerable seriesIds, string seriesDataPath, long? lastTvDbUpdateTime, IProgress progress, CancellationToken cancellationToken)
{
var list = seriesIds.ToList();
var numComplete = 0;
@@ -263,7 +273,7 @@ namespace MediaBrowser.Providers.TV
{
try
{
- await UpdateSeries(seriesId, seriesDataPath, cancellationToken).ConfigureAwait(false);
+ await UpdateSeries(seriesId, seriesDataPath, lastTvDbUpdateTime, cancellationToken).ConfigureAwait(false);
}
catch (HttpException ex)
{
@@ -289,9 +299,10 @@ namespace MediaBrowser.Providers.TV
///
/// The id.
/// The series data path.
+ /// The last tv db update time.
/// The cancellation token.
/// Task.
- private Task UpdateSeries(string id, string seriesDataPath, CancellationToken cancellationToken)
+ private Task UpdateSeries(string id, string seriesDataPath, long? lastTvDbUpdateTime, CancellationToken cancellationToken)
{
_logger.Info("Updating series " + id);
@@ -299,7 +310,7 @@ namespace MediaBrowser.Providers.TV
Directory.CreateDirectory(seriesDataPath);
- return RemoteSeriesProvider.Current.DownloadSeriesZip(id, seriesDataPath, cancellationToken);
+ return RemoteSeriesProvider.Current.DownloadSeriesZip(id, seriesDataPath, lastTvDbUpdateTime, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs
index ea6cca6ff..6d3cbcf26 100644
--- a/MediaBrowser.Server.Mono/Program.cs
+++ b/MediaBrowser.Server.Mono/Program.cs
@@ -11,6 +11,9 @@ using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows;
+using System.Net;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
using Gtk;
using Gdk;
using System.Threading.Tasks;
@@ -90,12 +93,17 @@ namespace MediaBrowser.Server.Mono
}
}
+ private static RemoteCertificateValidationCallback _ignoreCertificates = new RemoteCertificateValidationCallback(delegate { return true; });
+
private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager)
{
// TODO: Show splash here
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
+ // Allow all https requests
+ ServicePointManager.ServerCertificateValidationCallback = _ignoreCertificates;
+
_appHost = new ApplicationHost(appPaths, logManager);
var task = _appHost.Init();
@@ -264,4 +272,12 @@ namespace MediaBrowser.Server.Mono
Shutdown ();
}
}
+
+ class NoCheckCertificatePolicy : ICertificatePolicy
+ {
+ public bool CheckValidationResult (ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
+ {
+ return true;
+ }
+ }
}