New: Reanalyze media files if file size changes

Fixes #6757
Fixes #6765
Fixes #4482

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
This commit is contained in:
Qstick 2021-11-28 15:42:44 -06:00
parent 507e8ec814
commit c538424229
5 changed files with 94 additions and 27 deletions

View File

@ -45,6 +45,10 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Mocker.GetMock<IRootFolderService>()
.Setup(s => s.GetBestRootFolderPath(It.IsAny<string>()))
.Returns(_rootFolder);
Mocker.GetMock<IMediaFileService>()
.Setup(s => s.GetFilesByMovie(It.IsAny<int>()))
.Returns(new List<MovieFile>());
}
private void GivenRootFolder(params string[] subfolders)
@ -117,7 +121,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
.Verify(v => v.Clean(It.IsAny<Movie>(), It.IsAny<List<string>>()), Times.Never());
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.IsAny<List<string>>(), _movie), Times.Never());
.Verify(v => v.GetImportDecisions(It.IsAny<List<string>>(), _movie, false), Times.Never());
}
[Test]
@ -152,7 +156,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, false), Times.Once());
}
[Test]
@ -177,7 +181,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, false), Times.Once());
}
[Test]
@ -197,7 +201,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, false), Times.Once());
}
[Test]
@ -229,7 +233,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
.Verify(v => v.Clean(It.IsAny<Movie>(), It.IsAny<List<string>>()), Times.Once());
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.IsAny<List<string>>(), _movie), Times.Never());
.Verify(v => v.GetImportDecisions(It.IsAny<List<string>>(), _movie, false), Times.Never());
}
[Test]
@ -247,7 +251,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, false), Times.Once());
}
[Test]
@ -270,7 +274,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 4), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 4), _movie, false), Times.Once());
}
[Test]
@ -289,7 +293,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, false), Times.Once());
}
[Test]
@ -309,7 +313,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, false), Times.Once());
}
[Test]
@ -326,7 +330,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, false), Times.Once());
}
[Test]
@ -343,7 +347,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, false), Times.Once());
}
[Test]
@ -362,7 +366,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 2), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 2), _movie, false), Times.Once());
}
[Test]
@ -379,7 +383,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 2), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 2), _movie, false), Times.Once());
}
[Test]
@ -397,7 +401,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, false), Times.Once());
}
[Test]
@ -414,7 +418,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_movie);
Mocker.GetMock<IMakeImportDecision>()
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, false), Times.Once());
}
}
}

View File

@ -11,6 +11,7 @@ using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles.Commands;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.MediaFiles.MovieImport;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
@ -36,8 +37,10 @@ namespace NzbDrone.Core.MediaFiles
private readonly IImportApprovedMovie _importApprovedMovies;
private readonly IConfigService _configService;
private readonly IMovieService _movieService;
private readonly IMediaFileService _mediaFileService;
private readonly IMediaFileTableCleanupService _mediaFileTableCleanupService;
private readonly IRootFolderService _rootFolderService;
private readonly IUpdateMediaInfo _updateMediaInfoService;
private readonly IEventAggregator _eventAggregator;
private readonly Logger _logger;
@ -46,8 +49,10 @@ namespace NzbDrone.Core.MediaFiles
IImportApprovedMovie importApprovedMovies,
IConfigService configService,
IMovieService movieService,
IMediaFileService mediaFileService,
IMediaFileTableCleanupService mediaFileTableCleanupService,
IRootFolderService rootFolderService,
IUpdateMediaInfo updateMediaInfoService,
IEventAggregator eventAggregator,
Logger logger)
{
@ -56,8 +61,10 @@ namespace NzbDrone.Core.MediaFiles
_importApprovedMovies = importApprovedMovies;
_configService = configService;
_movieService = movieService;
_mediaFileService = mediaFileService;
_mediaFileTableCleanupService = mediaFileTableCleanupService;
_rootFolderService = rootFolderService;
_updateMediaInfoService = updateMediaInfoService;
_eventAggregator = eventAggregator;
_logger = logger;
}
@ -126,12 +133,46 @@ namespace NzbDrone.Core.MediaFiles
CleanMediaFiles(movie, mediaFileList);
var movieFiles = _mediaFileService.GetFilesByMovie(movie.Id);
var unmappedFiles = MediaFileService.FilterExistingFiles(mediaFileList, movieFiles, movie);
var decisionsStopwatch = Stopwatch.StartNew();
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, movie);
var decisions = _importDecisionMaker.GetImportDecisions(unmappedFiles, movie, false);
decisionsStopwatch.Stop();
_logger.Trace("Import decisions complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed);
_importApprovedMovies.Import(decisions, false);
// Update existing files that have a different file size
var fileInfoStopwatch = Stopwatch.StartNew();
var filesToUpdate = new List<MovieFile>();
foreach (var file in movieFiles)
{
var path = Path.Combine(movie.Path, file.RelativePath);
var fileSize = _diskProvider.GetFileSize(path);
if (file.Size == fileSize)
{
continue;
}
file.Size = fileSize;
if (!_updateMediaInfoService.Update(file, movie))
{
filesToUpdate.Add(file);
}
}
// Update any files that had a file size change, but didn't get media info updated.
if (filesToUpdate.Any())
{
_mediaFileService.Update(filesToUpdate);
}
fileInfoStopwatch.Stop();
_logger.Trace("Reprocessing existing files complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed);
RemoveEmptyMovieFolder(movie.Path);
CompletedScanning(movie);
}

View File

@ -110,5 +110,17 @@ namespace NzbDrone.Core.MediaFiles
{
_mediaFileRepository.DeleteForMovies(message.Movies.Select(m => m.Id).ToList());
}
public static List<string> FilterExistingFiles(List<string> files, List<MovieFile> movieFiles, Movie movie)
{
var seriesFilePaths = movieFiles.Select(f => Path.Combine(movie.Path, f.RelativePath)).ToList();
if (!seriesFilePaths.Any())
{
return files;
}
return files.Except(seriesFilePaths, PathEqualityComparer.Instance).ToList();
}
}
}

View File

@ -11,10 +11,10 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
{
public interface IUpdateMediaInfo
{
void Update(MovieFile movieFile, Movie movie);
bool Update(MovieFile movieFile, Movie movie);
}
public class UpdateMediaInfoService : IHandle<MovieScannedEvent>, IUpdateMediaInfo
public class UpdateMediaInfoService : IUpdateMediaInfo, IHandle<MovieScannedEvent>
{
private readonly IDiskProvider _diskProvider;
private readonly IMediaFileService _mediaFileService;
@ -54,35 +54,39 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
}
}
public void Update(MovieFile movieFile, Movie movie)
public bool Update(MovieFile movieFile, Movie movie)
{
if (!_configService.EnableMediaInfo)
{
_logger.Debug("MediaInfo is disabled");
return;
return false;
}
UpdateMediaInfo(movieFile, movie);
return UpdateMediaInfo(movieFile, movie);
}
private void UpdateMediaInfo(MovieFile movieFile, Movie movie)
private bool UpdateMediaInfo(MovieFile movieFile, Movie movie)
{
var path = Path.Combine(movie.Path, movieFile.RelativePath);
if (!_diskProvider.FileExists(path))
{
_logger.Debug("Can't update MediaInfo because '{0}' does not exist", path);
return;
return false;
}
var updatedMediaInfo = _videoFileInfoReader.GetMediaInfo(path);
if (updatedMediaInfo != null)
if (updatedMediaInfo == null)
{
movieFile.MediaInfo = updatedMediaInfo;
_mediaFileService.Update(movieFile);
_logger.Debug("Updated MediaInfo for '{0}'", path);
return false;
}
movieFile.MediaInfo = updatedMediaInfo;
_mediaFileService.Update(movieFile);
_logger.Debug("Updated MediaInfo for '{0}'", path);
return true;
}
}
}

View File

@ -16,6 +16,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
public interface IMakeImportDecision
{
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie);
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool filterExistingFiles);
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource);
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource, bool filterExistingFiles);
ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem downloadClientItem);
@ -53,6 +54,11 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
return GetImportDecisions(videoFiles, movie, null, null, false);
}
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool filterExistingFiles)
{
return GetImportDecisions(videoFiles, movie, null, null, false, filterExistingFiles);
}
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource)
{
return GetImportDecisions(videoFiles, movie, downloadClientItem, folderInfo, sceneSource, true);