Fix: Remove invalid metadata images

pull/2/head
Keivan Beigi 10 years ago
parent b907243bc0
commit c8993db2ad

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using FluentAssertions;
using NUnit.Framework;
@ -96,6 +97,20 @@ namespace NzbDrone.Common.Test.Http
response.Resource.Headers[header].ToString().Should().Be(value);
}
[Test]
public void should_not_download_file_with_error()
{
var file = GetTempFilePath();
Assert.Throws<WebException>(() => Subject.DownloadFile("http://download.sonarr.tv/wrongpath", file));
File.Exists(file).Should().BeFalse();
ExceptionVerification.ExpectedWarns(1);
}
}
public class HttpBinResource

@ -0,0 +1,25 @@
<html>
<body>
<h1>Directory browsing not allowed</h1>
<script type="text/javascript">
//<![CDATA[
try{if (!window.CloudFlare) {var CloudFlare=[{verbose:0,p:0,byc:0,owlid:"cf",bag2:1,mirage2:0,oracle:0,paths:{cloudflare:"/cdn-cgi/nexp/dok2v=1613a3a185/"},atok:"bcfb17b7a9a6273f7f0e198018af2ede",petok:"a7bb3b4bc466e3276777600e2b737abbc9de6489-1420151565-1800",zone:"thetvdb.com",rocket:"0",apps:{"ga_key":{"ua":"UA-9131861-1","ga_bs":"2"}}}];!function(a,b){a=document.createElement("script"),b=document.getElementsByTagName("script")[0],a.async=!0,a.src="//ajax.cloudflare.com/cdn-cgi/nexp/dok2v=919620257c/cloudflare.min.js",b.parentNode.insertBefore(a,b)}()}}catch(e){};
//]]>
</script>
<script type="text/javascript">
/* <![CDATA[ */
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-9131861-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
(function(b){(function(a){"__CF"in b&&"DJS"in b.__CF?b.__CF.DJS.push(a):"addEventListener"in b?b.addEventListener("load",a,!1):b.attachEvent("onload",a)})(function(){"FB"in b&&"Event"in FB&&"subscribe"in FB.Event&&(FB.Event.subscribe("edge.create",function(a){_gaq.push(["_trackSocial","facebook","like",a])}),FB.Event.subscribe("edge.remove",function(a){_gaq.push(["_trackSocial","facebook","unlike",a])}),FB.Event.subscribe("message.send",function(a){_gaq.push(["_trackSocial","facebook","send",a])}));"twttr"in b&&"events"in twttr&&"bind"in twttr.events&&twttr.events.bind("tweet",function(a){if(a){var b;if(a.target&&a.target.nodeName=="IFRAME")a:{if(a=a.target.src){a=a.split("#")[0].match(/[^?=&]+=([^&]*)?/g);b=0;for(var c;c=a[b];++b)if(c.indexOf("url")===0){b=unescape(c.split("=")[1]);break a}}b=void 0}_gaq.push(["_trackSocial","twitter","tweet",b])}})})})(window);
/* ]]> */
</script>
</body>
</html>

@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Metadata;
using NzbDrone.Core.Metadata.Files;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.HealthCheck.Checks
{
[TestFixture]
public class DeleteBadMediaCoversFixture : CoreTest<DeleteBadMediaCovers>
{
private List<MetadataFile> _metaData;
private List<Series> _series;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateListOfSize(1)
.All()
.With(c => c.Path = "C:\\TV\\".AsOsAgnostic())
.Build().ToList();
_metaData = Builder<MetadataFile>.CreateListOfSize(1)
.Build().ToList();
Mocker.GetMock<ISeriesService>()
.Setup(c => c.GetAllSeries())
.Returns(_series);
Mocker.GetMock<IMetadataFileService>()
.Setup(c => c.GetFilesBySeries(_series.First().Id))
.Returns(_metaData);
Mocker.GetMock<IConfigService>().SetupGet(c => c.CleanupMetadataImages).Returns(true);
}
[Test]
public void should_not_process_non_image_files()
{
_metaData.First().RelativePath = "season\\file.xml".AsOsAgnostic();
_metaData.First().Type = MetadataType.EpisodeMetadata;
Subject.Clean();
Mocker.GetMock<IDiskProvider>().Verify(c => c.StreamFile(It.IsAny<string>()), Times.Never());
}
[Test]
public void should_not_process_images_before_tvdb_switch()
{
_metaData.First().LastUpdated = new DateTime(2014, 12, 25);
Subject.Clean();
Mocker.GetMock<IDiskProvider>().Verify(c => c.StreamFile(It.IsAny<string>()), Times.Never());
}
[Test]
public void should_not_run_if_flag_is_false()
{
Mocker.GetMock<IConfigService>().SetupGet(c => c.CleanupMetadataImages).Returns(false);
Subject.Clean();
Mocker.GetMock<IConfigService>().VerifySet(c => c.CleanupMetadataImages = true, Times.Never());
Mocker.GetMock<ISeriesService>().Verify(c => c.GetAllSeries(), Times.Never());
AssertImageWasNotRemoved();
}
[Test]
public void should_set_clean_flag_to_false()
{
_metaData.First().LastUpdated = new DateTime(2014, 12, 25);
Subject.Clean();
Mocker.GetMock<IConfigService>().VerifySet(c => c.CleanupMetadataImages = false, Times.Once());
}
[Test]
public void should_delete_html_images()
{
var imagePath = "C:\\TV\\Season\\image.jpg".AsOsAgnostic();
_metaData.First().LastUpdated = new DateTime(2014, 12, 29);
_metaData.First().RelativePath = "Season\\image.jpg".AsOsAgnostic();
_metaData.First().Type = MetadataType.SeriesImage;
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.StreamFile(imagePath))
.Returns(new FileStream("Files\\html_image.jpg".AsOsAgnostic(), FileMode.Open, FileAccess.Read));
Subject.Clean();
Mocker.GetMock<IDiskProvider>().Verify(c => c.DeleteFile(imagePath), Times.Once());
Mocker.GetMock<IMetadataFileService>().Verify(c => c.Delete(_metaData.First().Id), Times.Once());
}
[Test]
public void should_delete_empty_images()
{
var imagePath = "C:\\TV\\Season\\image.jpg".AsOsAgnostic();
_metaData.First().LastUpdated = new DateTime(2014, 12, 29);
_metaData.First().Type = MetadataType.SeasonImage;
_metaData.First().RelativePath = "Season\\image.jpg".AsOsAgnostic();
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.StreamFile(imagePath))
.Returns(new FileStream("Files\\emptyfile.txt".AsOsAgnostic(), FileMode.Open, FileAccess.Read));
Subject.Clean();
Mocker.GetMock<IDiskProvider>().Verify(c => c.DeleteFile(imagePath), Times.Once());
Mocker.GetMock<IMetadataFileService>().Verify(c => c.Delete(_metaData.First().Id), Times.Once());
}
[Test]
public void should_not_delete_non_html_files()
{
var imagePath = "C:\\TV\\Season\\image.jpg".AsOsAgnostic();
_metaData.First().LastUpdated = new DateTime(2014, 12, 29);
_metaData.First().RelativePath = "Season\\image.jpg".AsOsAgnostic();
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.StreamFile(imagePath))
.Returns(new FileStream("Files\\Queue.txt".AsOsAgnostic(), FileMode.Open, FileAccess.Read));
Subject.Clean();
AssertImageWasNotRemoved();
}
private void AssertImageWasNotRemoved()
{
Mocker.GetMock<IDiskProvider>().Verify(c => c.DeleteFile(It.IsAny<string>()), Times.Never());
Mocker.GetMock<IMetadataFileService>().Verify(c => c.Delete(It.IsAny<int>()), Times.Never());
}
}
}

@ -165,6 +165,7 @@
<Compile Include="Framework\MigrationTest.cs" />
<Compile Include="Framework\NBuilderExtensions.cs" />
<Compile Include="Framework\TestDbHelper.cs" />
<Compile Include="HealthCheck\Checks\DeleteBadMediaCovers.cs" />
<Compile Include="HealthCheck\Checks\AppDataLocationFixture.cs" />
<Compile Include="HealthCheck\Checks\DownloadClientCheckFixture.cs" />
<Compile Include="HealthCheck\Checks\DroneFactoryCheckFixture.cs" />
@ -352,6 +353,12 @@
<None Include="Files\Indexers\BroadcastheNet\RecentFeed.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Include="Files\html_image.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Files\emptyfile.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Files\RSS\fanzub.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

@ -282,6 +282,13 @@ namespace NzbDrone.Core.Configuration
set { SetValue("ShowRelativeDates", value); }
}
public bool CleanupMetadataImages
{
get { return GetValueBoolean("CleanupMetadataImages", true); }
set { SetValue("CleanupMetadataImages", value); }
}
private string GetValue(string key)
{
return GetValue(key, String.Empty);

@ -55,5 +55,10 @@ namespace NzbDrone.Core.Configuration
String LongDateFormat { get; set; }
String TimeFormat { get; set; }
Boolean ShowRelativeDates { get; set; }
//Internal
Boolean CleanupMetadataImages { get; set; }
}
}

@ -1,23 +1,18 @@
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class CleanupAdditionalNamingSpecs : IHousekeepingTask
{
private readonly IDatabase _database;
private readonly Logger _logger;
public CleanupAdditionalNamingSpecs(IDatabase database, Logger logger)
public CleanupAdditionalNamingSpecs(IDatabase database)
{
_database = database;
_logger = logger;
}
public void Clean()
{
_logger.Debug("Running naming spec cleanup");
var mapper = _database.GetDataMapper();
mapper.ExecuteNonQuery(@"DELETE FROM NamingConfig

@ -1,23 +1,18 @@
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class CleanupDuplicateMetadataFiles : IHousekeepingTask
{
private readonly IDatabase _database;
private readonly Logger _logger;
public CleanupDuplicateMetadataFiles(IDatabase database, Logger logger)
public CleanupDuplicateMetadataFiles(IDatabase database)
{
_database = database;
_logger = logger;
}
public void Clean()
{
_logger.Debug("Running cleanup of duplicate metadata files");
DeleteDuplicateSeriesMetadata();
DeleteDuplicateEpisodeMetadata();
DeleteDuplicateEpisodeImages();

@ -1,23 +1,18 @@
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class CleanupOrphanedBlacklist : IHousekeepingTask
{
private readonly IDatabase _database;
private readonly Logger _logger;
public CleanupOrphanedBlacklist(IDatabase database, Logger logger)
public CleanupOrphanedBlacklist(IDatabase database)
{
_database = database;
_logger = logger;
}
public void Clean()
{
_logger.Debug("Running orphaned blacklist cleanup");
var mapper = _database.GetDataMapper();
mapper.ExecuteNonQuery(@"DELETE FROM Blacklist

@ -6,18 +6,14 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public class CleanupOrphanedEpisodeFiles : IHousekeepingTask
{
private readonly IDatabase _database;
private readonly Logger _logger;
public CleanupOrphanedEpisodeFiles(IDatabase database, Logger logger)
public CleanupOrphanedEpisodeFiles(IDatabase database)
{
_database = database;
_logger = logger;
}
public void Clean()
{
_logger.Debug("Running orphaned episode files cleanup");
var mapper = _database.GetDataMapper();
mapper.ExecuteNonQuery(@"DELETE FROM EpisodeFiles

@ -6,18 +6,14 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public class CleanupOrphanedEpisodes : IHousekeepingTask
{
private readonly IDatabase _database;
private readonly Logger _logger;
public CleanupOrphanedEpisodes(IDatabase database, Logger logger)
public CleanupOrphanedEpisodes(IDatabase database)
{
_database = database;
_logger = logger;
}
public void Clean()
{
_logger.Debug("Running orphaned episodes cleanup");
var mapper = _database.GetDataMapper();
mapper.ExecuteNonQuery(@"DELETE FROM Episodes

@ -1,22 +1,18 @@
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class CleanupOrphanedHistoryItems : IHousekeepingTask
{
private readonly IDatabase _database;
private readonly Logger _logger;
public CleanupOrphanedHistoryItems(IDatabase database, Logger logger)
public CleanupOrphanedHistoryItems(IDatabase database)
{
_database = database;
_logger = logger;
}
public void Clean()
{
_logger.Debug("Running orphaned history cleanup");
CleanupOrphanedBySeries();
CleanupOrphanedByEpisode();
}

@ -1,23 +1,18 @@
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class CleanupOrphanedMetadataFiles : IHousekeepingTask
{
private readonly IDatabase _database;
private readonly Logger _logger;
public CleanupOrphanedMetadataFiles(IDatabase database, Logger logger)
public CleanupOrphanedMetadataFiles(IDatabase database)
{
_database = database;
_logger = logger;
}
public void Clean()
{
_logger.Debug("Running orphaned episode files cleanup");
DeleteOrphanedBySeries();
DeleteOrphanedByEpisodeFile();
DeleteWhereEpisodeFileIsZero();

@ -1,23 +1,18 @@
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class CleanupOrphanedPendingReleases : IHousekeepingTask
{
private readonly IDatabase _database;
private readonly Logger _logger;
public CleanupOrphanedPendingReleases(IDatabase database, Logger logger)
public CleanupOrphanedPendingReleases(IDatabase database)
{
_database = database;
_logger = logger;
}
public void Clean()
{
_logger.Debug("Running orphaned pending releases cleanup");
var mapper = _database.GetDataMapper();
mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases

@ -0,0 +1,83 @@
using System;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Metadata.Files;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class DeleteBadMediaCovers : IHousekeepingTask
{
private readonly ISeriesService _seriesService;
private readonly IMetadataFileService _metadataFileService;
private readonly IDiskProvider _diskProvider;
private readonly IConfigService _configService;
private readonly Logger _logger;
public DeleteBadMediaCovers(ISeriesService seriesService, IMetadataFileService metadataFileService, IDiskProvider diskProvider, IConfigService configService, Logger logger)
{
_seriesService = seriesService;
_metadataFileService = metadataFileService;
_diskProvider = diskProvider;
_configService = configService;
_logger = logger;
}
public void Clean()
{
if (!_configService.CleanupMetadataImages) return;
var series = _seriesService.GetAllSeries();
foreach (var show in series)
{
var images = _metadataFileService.GetFilesBySeries(show.Id)
.Where(c => c.LastUpdated > new DateTime(2014, 12, 27) && c.RelativePath.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase));
foreach (var image in images)
{
try
{
var path = Path.Combine(show.Path, image.RelativePath);
if (!IsValid(path))
{
_logger.Debug("Deleting invalid image file " + path);
DeleteMetadata(image.Id, path);
}
}
catch (Exception e)
{
_logger.ErrorException("Couldn't validate image " + image.RelativePath, e);
}
}
}
_configService.CleanupMetadataImages = false;
}
private void DeleteMetadata(int id, string path)
{
_metadataFileService.Delete(id);
_diskProvider.DeleteFile(path);
}
private bool IsValid(string path)
{
var buffer = new byte[10];
using (var imageStream = _diskProvider.StreamFile(path))
{
if (imageStream.Length < buffer.Length) return false;
imageStream.Read(buffer, 0, buffer.Length);
}
var text = System.Text.Encoding.Default.GetString(buffer);
return !text.ToLowerInvariant().Contains("html");
}
}
}

@ -23,8 +23,6 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
_logger.Debug("Not running scheduled task last execution cleanup during debug");
}
_logger.Debug("Running scheduled task last execution cleanup");
var mapper = _database.GetDataMapper();
mapper.AddParameter("time", DateTime.UtcNow);

@ -1,63 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Newznab;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class UpdateAnimeCategories : IHousekeepingTask
{
private readonly IIndexerFactory _indexerFactory;
private readonly Logger _logger;
private const int NZBS_ORG_ANIME_ID = 7040;
private const int NEWZNAB_ANIME_ID = 5070;
public UpdateAnimeCategories(IIndexerFactory indexerFactory, Logger logger)
{
_indexerFactory = indexerFactory;
_logger = logger;
}
public void Clean()
{
//TODO: We should remove this before merging it into develop
_logger.Debug("Updating Anime Categories for newznab indexers");
var indexers = _indexerFactory.All().Where(i => i.Implementation == typeof (Newznab).Name);
foreach (var indexer in indexers)
{
var settings = indexer.Settings as NewznabSettings;
if (settings.Url.ContainsIgnoreCase("nzbs.org") && settings.Categories.Contains(NZBS_ORG_ANIME_ID))
{
var animeCategories = new List<int>(settings.AnimeCategories);
animeCategories.Add(NZBS_ORG_ANIME_ID);
settings.AnimeCategories = animeCategories;
settings.Categories = settings.Categories.Where(c => c != NZBS_ORG_ANIME_ID);
indexer.Settings = settings;
_indexerFactory.Update(indexer);
}
else if (settings.Categories.Contains(NEWZNAB_ANIME_ID))
{
var animeCategories = new List<int>(settings.AnimeCategories);
animeCategories.Add(NEWZNAB_ANIME_ID);
settings.AnimeCategories = animeCategories;
settings.Categories = settings.Categories.Where(c => c != NEWZNAB_ANIME_ID);
indexer.Settings = settings;
_indexerFactory.Update(indexer);
}
}
}
}
}

@ -1,5 +1,4 @@
using System.Linq;
using NLog;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Tv;
@ -8,18 +7,14 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public class UpdateCleanTitleForSeries : IHousekeepingTask
{
private readonly ISeriesRepository _seriesRepository;
private readonly Logger _logger;
public UpdateCleanTitleForSeries(ISeriesRepository seriesRepository, Logger logger)
public UpdateCleanTitleForSeries(ISeriesRepository seriesRepository)
{
_seriesRepository = seriesRepository;
_logger = logger;
}
public void Clean()
{
_logger.Debug("Updating CleanTitle for all series");
var series = _seriesRepository.All().ToList();
series.ForEach(s =>

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
@ -30,22 +29,21 @@ namespace NzbDrone.Core.Housekeeping
{
try
{
_logger.Debug("Starting {0}", housekeeper.GetType().Name);
housekeeper.Clean();
_logger.Debug("Completed {0}", housekeeper.GetType().Name);
}
catch (Exception ex)
{
_logger.ErrorException("Error running housekeeping task: " + housekeeper.GetType().FullName, ex);
_logger.ErrorException("Error running housekeeping task: " + housekeeper.GetType().Name, ex);
}
}
//Only Vaccuum the DB in production
if (RuntimeInfoBase.IsProduction)
{
// Vacuuming the log db isn't needed since that's done hourly at the TrimLogCommand.
_logger.Debug("Compressing main database after housekeeping");
_mainDb.Vacuum();
}
}
public void Execute(HousekeepingCommand message)
{

@ -415,8 +415,8 @@
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedHistoryItems.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedMetadataFiles.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedPendingReleases.cs" />
<Compile Include="Housekeeping\Housekeepers\DeleteBadMediaCovers.cs" />
<Compile Include="Housekeeping\Housekeepers\FixFutureRunScheduledTasks.cs" />
<Compile Include="Housekeeping\Housekeepers\UpdateAnimeCategories.cs" />
<Compile Include="Housekeeping\Housekeepers\UpdateCleanTitleForSeries.cs" />
<Compile Include="Housekeeping\HousekeepingCommand.cs" />
<Compile Include="Housekeeping\HousekeepingService.cs" />

Loading…
Cancel
Save