Switch to using stdout for information

Also fixes dependency loop
This commit is contained in:
Jendrik Weise 2023-09-03 04:38:02 +02:00
parent d61f119f8b
commit 6d1bc33502
6 changed files with 76 additions and 73 deletions

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.IO; using System.IO;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
@ -71,7 +72,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenFileExists(); GivenFileExists();
GivenSuccessfulScan(); GivenSuccessfulScan();
Subject.Handle(new SeriesScannedEvent(_series)); Subject.Handle(new SeriesScannedEvent(_series, new List<string>()));
Mocker.GetMock<IVideoFileInfoReader>() Mocker.GetMock<IVideoFileInfoReader>()
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(2)); .Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(2));
@ -97,7 +98,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenFileExists(); GivenFileExists();
GivenSuccessfulScan(); GivenSuccessfulScan();
Subject.Handle(new SeriesScannedEvent(_series)); Subject.Handle(new SeriesScannedEvent(_series, new List<string>()));
Mocker.GetMock<IVideoFileInfoReader>() Mocker.GetMock<IVideoFileInfoReader>()
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(2)); .Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(2));
@ -123,7 +124,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenFileExists(); GivenFileExists();
GivenSuccessfulScan(); GivenSuccessfulScan();
Subject.Handle(new SeriesScannedEvent(_series)); Subject.Handle(new SeriesScannedEvent(_series, new List<string>()));
Mocker.GetMock<IVideoFileInfoReader>() Mocker.GetMock<IVideoFileInfoReader>()
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(3)); .Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(3));
@ -146,7 +147,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenSuccessfulScan(); GivenSuccessfulScan();
Subject.Handle(new SeriesScannedEvent(_series)); Subject.Handle(new SeriesScannedEvent(_series, new List<string>()));
Mocker.GetMock<IVideoFileInfoReader>() Mocker.GetMock<IVideoFileInfoReader>()
.Verify(v => v.GetMediaInfo("media.mkv"), Times.Never()); .Verify(v => v.GetMediaInfo("media.mkv"), Times.Never());
@ -173,7 +174,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenSuccessfulScan(); GivenSuccessfulScan();
GivenFailedScan(Path.Combine(_series.Path, "media2.mkv")); GivenFailedScan(Path.Combine(_series.Path, "media2.mkv"));
Subject.Handle(new SeriesScannedEvent(_series)); Subject.Handle(new SeriesScannedEvent(_series, new List<string>()));
Mocker.GetMock<IVideoFileInfoReader>() Mocker.GetMock<IVideoFileInfoReader>()
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(1)); .Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(1));
@ -203,7 +204,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenFileExists(); GivenFileExists();
GivenSuccessfulScan(); GivenSuccessfulScan();
Subject.Handle(new SeriesScannedEvent(_series)); Subject.Handle(new SeriesScannedEvent(_series, new List<string>()));
Mocker.GetMock<IVideoFileInfoReader>() Mocker.GetMock<IVideoFileInfoReader>()
.Verify(v => v.GetMediaInfo(It.IsAny<string>()), Times.Never()); .Verify(v => v.GetMediaInfo(It.IsAny<string>()), Times.Never());

View File

@ -2,33 +2,25 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Extras namespace NzbDrone.Core.Extras
{ {
public interface IExistingExtraFiles : IHandle<SeriesScannedEvent> public interface IExistingExtraFiles
{ {
List<string> ImportFileList(Series series, List<string> possibleExtraFiles); List<string> ImportFileList(Series series, List<string> possibleExtraFiles);
} }
public class ExistingExtraFileService : IExistingExtraFiles public class ExistingExtraFileService : IExistingExtraFiles, IHandle<SeriesScannedEvent>
{ {
private readonly IDiskProvider _diskProvider;
private readonly IDiskScanService _diskScanService;
private readonly List<IImportExistingExtraFiles> _existingExtraFileImporters; private readonly List<IImportExistingExtraFiles> _existingExtraFileImporters;
private readonly Logger _logger; private readonly Logger _logger;
public ExistingExtraFileService(IDiskProvider diskProvider, public ExistingExtraFileService(IEnumerable<IImportExistingExtraFiles> existingExtraFileImporters,
IDiskScanService diskScanService,
IEnumerable<IImportExistingExtraFiles> existingExtraFileImporters,
Logger logger) Logger logger)
{ {
_diskProvider = diskProvider;
_diskScanService = diskScanService;
_existingExtraFileImporters = existingExtraFileImporters.OrderBy(e => e.Order).ToList(); _existingExtraFileImporters = existingExtraFileImporters.OrderBy(e => e.Order).ToList();
_logger = logger; _logger = logger;
} }
@ -53,13 +45,7 @@ namespace NzbDrone.Core.Extras
{ {
var series = message.Series; var series = message.Series;
if (!_diskProvider.FolderExists(series.Path)) var possibleExtraFiles = message.PossibleExtraFiles;
{
return;
}
var filesOnDisk = _diskScanService.GetNonVideoFiles(series.Path);
var possibleExtraFiles = _diskScanService.FilterPaths(series.Path, filesOnDisk);
var importedFiles = ImportFileList(series, possibleExtraFiles); var importedFiles = ImportFileList(series, possibleExtraFiles);

View File

@ -121,7 +121,7 @@ namespace NzbDrone.Core.MediaFiles
} }
CleanMediaFiles(series, new List<string>()); CleanMediaFiles(series, new List<string>());
CompletedScanning(series); CompletedScanning(series, new List<string>());
return; return;
} }
@ -174,8 +174,11 @@ namespace NzbDrone.Core.MediaFiles
fileInfoStopwatch.Stop(); fileInfoStopwatch.Stop();
_logger.Trace("Reprocessing existing files complete for: {0} [{1}]", series, decisionsStopwatch.Elapsed); _logger.Trace("Reprocessing existing files complete for: {0} [{1}]", series, decisionsStopwatch.Elapsed);
var filesOnDisk = GetNonVideoFiles(series.Path);
var possibleExtraFiles = FilterPaths(series.Path, filesOnDisk);
RemoveEmptySeriesFolder(series.Path); RemoveEmptySeriesFolder(series.Path);
CompletedScanning(series); CompletedScanning(series, possibleExtraFiles);
} }
private void CleanMediaFiles(Series series, List<string> mediaFileList) private void CleanMediaFiles(Series series, List<string> mediaFileList)
@ -184,10 +187,10 @@ namespace NzbDrone.Core.MediaFiles
_mediaFileTableCleanupService.Clean(series, mediaFileList); _mediaFileTableCleanupService.Clean(series, mediaFileList);
} }
private void CompletedScanning(Series series) private void CompletedScanning(Series series, List<string> possibleExtraFiles)
{ {
_logger.Info("Completed scanning disk for {0}", series.Title); _logger.Info("Completed scanning disk for {0}", series.Title);
_eventAggregator.PublishEvent(new SeriesScannedEvent(series)); _eventAggregator.PublishEvent(new SeriesScannedEvent(series, possibleExtraFiles));
} }
public string[] GetVideoFiles(string path, bool allDirectories = true) public string[] GetVideoFiles(string path, bool allDirectories = true)

View File

@ -1,4 +1,5 @@
using NzbDrone.Common.Messaging; using System.Collections.Generic;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.MediaFiles.Events namespace NzbDrone.Core.MediaFiles.Events
@ -6,10 +7,12 @@ namespace NzbDrone.Core.MediaFiles.Events
public class SeriesScannedEvent : IEvent public class SeriesScannedEvent : IEvent
{ {
public Series Series { get; private set; } public Series Series { get; private set; }
public List<string> PossibleExtraFiles { get; set; }
public SeriesScannedEvent(Series series) public SeriesScannedEvent(Series series, List<string> possibleExtraFiles)
{ {
Series = series; Series = series;
PossibleExtraFiles = possibleExtraFiles;
} }
} }
} }

View File

@ -2,12 +2,13 @@ using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Processes; using NzbDrone.Common.Processes;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Extras; using NzbDrone.Core.Extras.Subtitles;
using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -27,7 +28,7 @@ namespace NzbDrone.Core.MediaFiles
private readonly IProcessProvider _processProvider; private readonly IProcessProvider _processProvider;
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly ITagRepository _tagRepository; private readonly ITagRepository _tagRepository;
private readonly IExistingExtraFiles _existingExtraFiles; private readonly IDiskProvider _diskProvider;
private readonly Logger _logger; private readonly Logger _logger;
public ImportScriptService(IProcessProvider processProvider, public ImportScriptService(IProcessProvider processProvider,
@ -35,7 +36,7 @@ namespace NzbDrone.Core.MediaFiles
IConfigService configService, IConfigService configService,
IConfigFileProvider configFileProvider, IConfigFileProvider configFileProvider,
ITagRepository tagRepository, ITagRepository tagRepository,
IExistingExtraFiles existingExtraFiles, IDiskProvider diskProvider,
Logger logger) Logger logger)
{ {
_processProvider = processProvider; _processProvider = processProvider;
@ -43,61 +44,57 @@ namespace NzbDrone.Core.MediaFiles
_configService = configService; _configService = configService;
_configFileProvider = configFileProvider; _configFileProvider = configFileProvider;
_tagRepository = tagRepository; _tagRepository = tagRepository;
_existingExtraFiles = existingExtraFiles; _diskProvider = diskProvider;
_logger = logger; _logger = logger;
} }
private (List<string> possibleMediaFiles, string mediaFile) ProcessTemporaryFile(string temporaryPath) private static readonly Regex MediaFileLine = new Regex(@"^\[SonarrMediaFile\]\s*(?<mediaFileName>.*)$", RegexOptions.Compiled);
private static readonly Regex ExtraFileLine = new Regex(@"^\[SonarrExtraFile\]\s*(?<extraFileName>.*)$", RegexOptions.Compiled);
private ScriptImportInfo ProcessStdout(List<ProcessOutputLine> processOutputLines, string defaultMediaFile)
{ {
var lines = File.ReadAllLines(temporaryPath);
if (lines.Length == 0 || string.IsNullOrWhiteSpace(lines[0]))
{
return (new List<string>(), null);
}
var possibleExtraFiles = new List<string>(); var possibleExtraFiles = new List<string>();
string mediaFile = null; string mediaFile = null;
foreach (var line in lines) foreach (var line in processOutputLines)
{ {
if (MediaFileExtensions.Extensions.Contains(Path.GetExtension(line))) if (MediaFileLine.Match(line.Content) is var match && match.Success)
{ {
if (mediaFile is not null) if (mediaFile is not null)
{ {
throw new ScriptImportException("Script output contains multiple media files. Only one media file can be returned."); throw new ScriptImportException("Script output contains multiple media files. Only one media file can be returned.");
} }
else
mediaFile = match.Groups["mediaFileName"].Value;
if (!MediaFileExtensions.Extensions.Contains(Path.GetExtension(mediaFile)))
{ {
if (File.Exists(line)) throw new ScriptImportException("Script output contains invalid media file: {0}", mediaFile);
{ }
mediaFile = line; else if (!_diskProvider.FileExists(mediaFile))
} {
else throw new ScriptImportException("Script output contains non-existent media file: {0}", mediaFile);
{
_logger.Warn("Script output contains invalid file: {0}", line);
}
} }
} }
else else if (ExtraFileLine.Match(line.Content) is var match2 && match2.Success)
{ {
if (File.Exists(line)) possibleExtraFiles.Add(match2.Groups["extraFileName"].Value);
var lastAdded = possibleExtraFiles.Last();
if (!SubtitleFileExtensions.Extensions.Contains(Path.GetExtension(lastAdded)))
{ {
possibleExtraFiles.Add(line); throw new ScriptImportException("Script output contains invalid extra file: {0}", lastAdded);
} }
else else if (!_diskProvider.FileExists(lastAdded))
{ {
_logger.Warn("Script output contains invalid file: {0}", line); throw new ScriptImportException("Script output contains non-existent extra file: {0}", lastAdded);
} }
} }
} }
if (mediaFile is not null) return new ScriptImportInfo(possibleExtraFiles, mediaFile ?? defaultMediaFile);
{
throw new ScriptImportException("Script output does not contain a media file.");
}
return (possibleExtraFiles, mediaFile);
} }
public ScriptImportDecision TryImport(string sourcePath, string destinationFilePath, LocalEpisode localEpisode, EpisodeFile episodeFile, TransferMode mode) public ScriptImportDecision TryImport(string sourcePath, string destinationFilePath, LocalEpisode localEpisode, EpisodeFile episodeFile, TransferMode mode)
@ -112,13 +109,10 @@ namespace NzbDrone.Core.MediaFiles
return ScriptImportDecision.DeferMove; return ScriptImportDecision.DeferMove;
} }
var temporaryPath = Path.GetTempFileName();
var environmentVariables = new StringDictionary(); var environmentVariables = new StringDictionary();
environmentVariables.Add("Sonarr_SourcePath", sourcePath); environmentVariables.Add("Sonarr_SourcePath", sourcePath);
environmentVariables.Add("Sonarr_DestinationPath", destinationFilePath); environmentVariables.Add("Sonarr_DestinationPath", destinationFilePath);
environmentVariables.Add("Sonarr_InfoFilePath", temporaryPath);
environmentVariables.Add("Sonarr_InstanceName", _configFileProvider.InstanceName); environmentVariables.Add("Sonarr_InstanceName", _configFileProvider.InstanceName);
environmentVariables.Add("Sonarr_ApplicationUrl", _configService.ApplicationUrl); environmentVariables.Add("Sonarr_ApplicationUrl", _configService.ApplicationUrl);
@ -179,13 +173,13 @@ namespace NzbDrone.Core.MediaFiles
_logger.Debug("Executed external script: {0} - Status: {1}", _configService.ScriptImportPath, processOutput.ExitCode); _logger.Debug("Executed external script: {0} - Status: {1}", _configService.ScriptImportPath, processOutput.ExitCode);
_logger.Debug("Script Output: \r\n{0}", string.Join("\r\n", processOutput.Lines)); _logger.Debug("Script Output: \r\n{0}", string.Join("\r\n", processOutput.Lines));
var (possibleExtraFiles, mediaFile) = ProcessTemporaryFile(temporaryPath); var scriptImportInfo = ProcessStdout(processOutput.Lines, destinationFilePath);
localEpisode.PossibleExtraFiles = possibleExtraFiles; var mediaFile = scriptImportInfo.MediaFile;
localEpisode.PossibleExtraFiles = scriptImportInfo.PossibleExtraFiles;
destinationFilePath = mediaFile ?? destinationFilePath; episodeFile.RelativePath = series.Path.GetRelativePath(mediaFile);
episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilePath); episodeFile.Path = mediaFile;
episodeFile.Path = destinationFilePath;
if ((processOutput.ExitCode & 0x4) == 0x4) if ((processOutput.ExitCode & 0x4) == 0x4)
{ {
@ -199,7 +193,7 @@ namespace NzbDrone.Core.MediaFiles
return ScriptImportDecision.MoveComplete; return ScriptImportDecision.MoveComplete;
case 2: // Copy complete, file potentially changed, should try renaming again case 2: // Copy complete, file potentially changed, should try renaming again
localEpisode.ScriptImported = true; localEpisode.ScriptImported = true;
episodeFile.MediaInfo = _videoFileInfoReader.GetMediaInfo(destinationFilePath); episodeFile.MediaInfo = _videoFileInfoReader.GetMediaInfo(mediaFile);
episodeFile.Path = null; episodeFile.Path = null;
return ScriptImportDecision.RenameRequested; return ScriptImportDecision.RenameRequested;
case 3: // Let Sonarr handle it case 3: // Let Sonarr handle it

View File

@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace NzbDrone.Core.MediaFiles
{
public struct ScriptImportInfo
{
public List<string> PossibleExtraFiles { get; set; }
public string MediaFile { get; set; }
public ScriptImportInfo(List<string> possibleExtraFiles, string mediaFile)
{
PossibleExtraFiles = possibleExtraFiles;
MediaFile = mediaFile;
}
}
}