diff --git a/src/NzbDrone.Common/Http/HttpHeader.cs b/src/NzbDrone.Common/Http/HttpHeader.cs index 747015b93..46180515f 100644 --- a/src/NzbDrone.Common/Http/HttpHeader.cs +++ b/src/NzbDrone.Common/Http/HttpHeader.cs @@ -117,6 +117,18 @@ namespace NzbDrone.Common.Http } } + public DateTime? LastModified + { + get + { + return GetSingleValue("Last-Modified", Convert.ToDateTime); + } + set + { + SetSingleValue("Last-Modified", value); + } + } + public new IEnumerator> GetEnumerator() { return AllKeys.SelectMany(GetValues, (k, c) => new KeyValuePair(k, c)).ToList().GetEnumerator(); diff --git a/src/NzbDrone.Core.Test/MediaCoverTests/CoverExistsSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaCoverTests/CoverExistsSpecificationFixture.cs index bb3b0a99c..1cba3766c 100644 --- a/src/NzbDrone.Core.Test/MediaCoverTests/CoverExistsSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaCoverTests/CoverExistsSpecificationFixture.cs @@ -1,72 +1,51 @@ -using System.Net; using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; -using NzbDrone.Common.Http; using NzbDrone.Core.MediaCover; using NzbDrone.Core.Test.Framework; +using System; namespace NzbDrone.Core.Test.MediaCoverTests { [TestFixture] public class CoverAlreadyExistsSpecificationFixture : CoreTest { - private HttpResponse _httpResponse; - - [SetUp] - public void Setup() - { - _httpResponse = new HttpResponse(null, new HttpHeader(), "", HttpStatusCode.OK); - Mocker.GetMock().Setup(c => c.GetFileSize(It.IsAny())).Returns(100); - Mocker.GetMock().Setup(c => c.Head(It.IsAny())).Returns(_httpResponse); - - } - - private void GivenFileExistsOnDisk() { Mocker.GetMock().Setup(c => c.FileExists(It.IsAny())).Returns(true); } - private void GivenExistingFileSize(long bytes) + private void GivenExistingFileDate(DateTime lastModifiedDate) { GivenFileExistsOnDisk(); - Mocker.GetMock().Setup(c => c.GetFileSize(It.IsAny())).Returns(bytes); + Mocker.GetMock().Setup(c => c.FileGetLastWrite(It.IsAny())).Returns(lastModifiedDate); } - [Test] public void should_return_false_if_file_not_exists() { - Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeFalse(); + Subject.AlreadyExists(DateTime.Now, "c:\\file.exe").Should().BeFalse(); } [Test] - public void should_return_false_if_file_exists_but_diffrent_size() + public void should_return_false_if_file_exists_but_diffrent_date() { - GivenExistingFileSize(100); - _httpResponse.Headers.ContentLength = 200; + GivenExistingFileDate(DateTime.Now); - Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeFalse(); + Subject.AlreadyExists(DateTime.Now.AddHours(-5), "c:\\file.exe").Should().BeFalse(); } - [Test] - public void should_return_ture_if_file_exists_and_same_size() + public void should_return_ture_if_file_exists_and_same_date() { - GivenExistingFileSize(100); - _httpResponse.Headers.ContentLength = 100; - Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeTrue(); - } + var givenDate = DateTime.Now; - [Test] - public void should_return_true_if_there_is_no_size_header_and_file_exist() - { - GivenExistingFileSize(100); - Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeFalse(); + GivenExistingFileDate(givenDate); + + Subject.AlreadyExists(givenDate, "c:\\file.exe").Should().BeTrue(); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs b/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs index 6de8ece98..f604a9d83 100644 --- a/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs @@ -7,6 +7,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Http; using NzbDrone.Core.MediaCover; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Music; @@ -19,6 +20,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests { Artist _artist; Album _album; + private HttpResponse _httpResponse; [SetUp] public void Setup() @@ -34,6 +36,9 @@ namespace NzbDrone.Core.Test.MediaCoverTests .With(v => v.Id = 4) .With(v => v.Images = new List { new MediaCover.MediaCover(MediaCoverTypes.Cover, "") }) .Build(); + + _httpResponse = new HttpResponse(null, new HttpHeader(), ""); + Mocker.GetMock().Setup(c => c.Head(It.IsAny())).Returns(_httpResponse); } [Test] @@ -95,7 +100,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests public void should_resize_covers_if_main_downloaded() { Mocker.GetMock() - .Setup(v => v.AlreadyExists(It.IsAny(), It.IsAny())) + .Setup(v => v.AlreadyExists(It.IsAny(), It.IsAny())) .Returns(false); Mocker.GetMock() @@ -112,7 +117,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests public void should_resize_covers_if_missing() { Mocker.GetMock() - .Setup(v => v.AlreadyExists(It.IsAny(), It.IsAny())) + .Setup(v => v.AlreadyExists(It.IsAny(), It.IsAny())) .Returns(true); Mocker.GetMock() @@ -129,7 +134,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests public void should_not_resize_covers_if_exists() { Mocker.GetMock() - .Setup(v => v.AlreadyExists(It.IsAny(), It.IsAny())) + .Setup(v => v.AlreadyExists(It.IsAny(), It.IsAny())) .Returns(true); Mocker.GetMock() @@ -150,7 +155,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests public void should_resize_covers_if_existing_is_empty() { Mocker.GetMock() - .Setup(v => v.AlreadyExists(It.IsAny(), It.IsAny())) + .Setup(v => v.AlreadyExists(It.IsAny(), It.IsAny())) .Returns(true); Mocker.GetMock() @@ -171,7 +176,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests public void should_log_error_if_resize_failed() { Mocker.GetMock() - .Setup(v => v.AlreadyExists(It.IsAny(), It.IsAny())) + .Setup(v => v.AlreadyExists(It.IsAny(), It.IsAny())) .Returns(true); Mocker.GetMock() diff --git a/src/NzbDrone.Core/MediaCover/CoverAlreadyExistsSpecification.cs b/src/NzbDrone.Core/MediaCover/CoverAlreadyExistsSpecification.cs index 83f1e13de..fa97604f9 100644 --- a/src/NzbDrone.Core/MediaCover/CoverAlreadyExistsSpecification.cs +++ b/src/NzbDrone.Core/MediaCover/CoverAlreadyExistsSpecification.cs @@ -1,34 +1,32 @@ -using NzbDrone.Common.Disk; -using NzbDrone.Common.Http; +using System; +using NzbDrone.Common.Disk; namespace NzbDrone.Core.MediaCover { public interface ICoverExistsSpecification { - bool AlreadyExists(string url, string path); + bool AlreadyExists(DateTime serverModifiedDate, string localPath); } public class CoverAlreadyExistsSpecification : ICoverExistsSpecification { private readonly IDiskProvider _diskProvider; - private readonly IHttpClient _httpClient; - public CoverAlreadyExistsSpecification(IDiskProvider diskProvider, IHttpClient httpClient) + public CoverAlreadyExistsSpecification(IDiskProvider diskProvider) { _diskProvider = diskProvider; - _httpClient = httpClient; } - public bool AlreadyExists(string url, string path) + public bool AlreadyExists(DateTime lastModifiedDateServer, string localPath) { - if (!_diskProvider.FileExists(path)) + if (!_diskProvider.FileExists(localPath)) { return false; } - var headers = _httpClient.Head(new HttpRequest(url)).Headers; - var fileSize = _diskProvider.GetFileSize(path); - return fileSize == headers.ContentLength; + DateTime? lastModifiedLocal = _diskProvider.FileGetLastWrite(localPath); + + return lastModifiedLocal.Value.ToUniversalTime() == lastModifiedDateServer.ToUniversalTime(); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs index b477f0e85..44aabd3e5 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs @@ -109,12 +109,16 @@ namespace NzbDrone.Core.MediaCover { var fileName = GetCoverPath(artist.Id, cover.CoverType); var alreadyExists = false; + try { - alreadyExists = _coverExistsSpecification.AlreadyExists(cover.Url, fileName); + var lastModifiedServer = GetCoverModifiedDate(cover.Url); + + alreadyExists = _coverExistsSpecification.AlreadyExists(lastModifiedServer, fileName); + if (!alreadyExists) { - DownloadCover(artist, cover); + DownloadCover(artist, cover, lastModifiedServer); } } catch (WebException e) @@ -130,48 +134,58 @@ namespace NzbDrone.Core.MediaCover } } - private void EnsureAlbumCovers(Album album) - { - foreach (var cover in album.Images) - { - var fileName = GetCoverPath(album.ArtistId, cover.CoverType, null, album.Id); - var alreadyExists = false; - try - { - alreadyExists = _coverExistsSpecification.AlreadyExists(cover.Url, fileName); - if (!alreadyExists) - { - DownloadAlbumCover(album, cover); - } - } - catch (WebException e) - { - _logger.Warn("Couldn't download media cover for {0}. {1}", album, e.Message); - } - catch (Exception e) - { - _logger.Error(e, "Couldn't download media cover for {0}", album); - } - - EnsureResizedCovers(album.Artist, cover, !alreadyExists, album); - } - } - - private void DownloadCover(Artist artist, MediaCover cover) + //TODO Decide if we want to cache album art local + //private void EnsureAlbumCovers(Album album) + //{ + // foreach (var cover in album.Images) + // { + // var fileName = GetCoverPath(album.ArtistId, cover.CoverType, null, album.Id); + // var alreadyExists = false; + // try + // { + // alreadyExists = _coverExistsSpecification.AlreadyExists(cover.Url, fileName); + // if (!alreadyExists) + // { + // DownloadAlbumCover(album, cover); + // } + // } + // catch (WebException e) + // { + // _logger.Warn("Couldn't download media cover for {0}. {1}", album, e.Message); + // } + // catch (Exception e) + // { + // _logger.Error(e, "Couldn't download media cover for {0}", album); + // } + + // EnsureResizedCovers(album.Artist, cover, !alreadyExists, album); + // } + //} + + private void DownloadCover(Artist artist, MediaCover cover, DateTime lastModified) { var fileName = GetCoverPath(artist.Id, cover.CoverType); _logger.Info("Downloading {0} for {1} {2}", cover.CoverType, artist, cover.Url); _httpClient.DownloadFile(cover.Url, fileName); + + try + { + _diskProvider.FileSetLastWriteTime(fileName, lastModified); + } + catch (Exception ex) + { + _logger.Debug(ex, "Unable to set modified date for {0} image for artist {1}", cover.CoverType, artist); + } } - private void DownloadAlbumCover(Album album, MediaCover cover) - { - var fileName = GetCoverPath(album.ArtistId, cover.CoverType, null, album.Id); + //private void DownloadAlbumCover(Album album, MediaCover cover) + //{ + // var fileName = GetCoverPath(album.ArtistId, cover.CoverType, null, album.Id); - _logger.Info("Downloading {0} for {1} {2}", cover.CoverType, album, cover.Url); - _httpClient.DownloadFile(cover.Url, fileName); - } + // _logger.Info("Downloading {0} for {1} {2}", cover.CoverType, album, cover.Url); + // _httpClient.DownloadFile(cover.Url, fileName); + //} private void EnsureResizedCovers(Artist artist, MediaCover cover, bool forceResize, Album album = null) { @@ -270,5 +284,19 @@ namespace NzbDrone.Core.MediaCover _diskProvider.DeleteFolder(path, true); } } + + private DateTime GetCoverModifiedDate(string url) + { + var lastModifiedServer = DateTime.Now; + + var headers = _httpClient.Head(new HttpRequest(url)).Headers; + + if (headers.LastModified.HasValue) + { + lastModifiedServer = headers.LastModified.Value; + } + + return lastModifiedServer; + } } }