WIP: communication back from custom import script

This commit is contained in:
Jendrik Weise 2023-08-22 14:42:00 +02:00
parent 7986488c6d
commit 6d0f73e641
6 changed files with 127 additions and 13 deletions

View File

@ -6,10 +6,16 @@ using NzbDrone.Common.Disk;
using NzbDrone.Core.MediaFiles; 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;
namespace NzbDrone.Core.Extras namespace NzbDrone.Core.Extras
{ {
public class ExistingExtraFileService : IHandle<SeriesScannedEvent> public interface IExistingExtraFiles : IHandle<SeriesScannedEvent>
{
List<string> ImportFileList(Series series, List<string> possibleExtraFiles);
}
public class ExistingExtraFileService : IExistingExtraFiles
{ {
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly IDiskScanService _diskScanService; private readonly IDiskScanService _diskScanService;
@ -27,20 +33,10 @@ namespace NzbDrone.Core.Extras
_logger = logger; _logger = logger;
} }
public void Handle(SeriesScannedEvent message) public List<string> ImportFileList(Series series, List<string> possibleExtraFiles)
{ {
var series = message.Series;
if (!_diskProvider.FolderExists(series.Path))
{
return;
}
_logger.Debug("Looking for existing extra files in {0}", series.Path); _logger.Debug("Looking for existing extra files in {0}", series.Path);
var filesOnDisk = _diskScanService.GetNonVideoFiles(series.Path);
var possibleExtraFiles = _diskScanService.FilterPaths(series.Path, filesOnDisk);
var importedFiles = new List<string>(); var importedFiles = new List<string>();
foreach (var existingExtraFileImporter in _existingExtraFileImporters) foreach (var existingExtraFileImporter in _existingExtraFileImporters)
@ -50,6 +46,23 @@ namespace NzbDrone.Core.Extras
importedFiles.AddRange(imported.Select(f => Path.Combine(series.Path, f.RelativePath))); importedFiles.AddRange(imported.Select(f => Path.Combine(series.Path, f.RelativePath)));
} }
return importedFiles;
}
public void Handle(SeriesScannedEvent message)
{
var series = message.Series;
if (!_diskProvider.FolderExists(series.Path))
{
return;
}
var filesOnDisk = _diskScanService.GetNonVideoFiles(series.Path);
var possibleExtraFiles = _diskScanService.FilterPaths(series.Path, filesOnDisk);
var importedFiles = ImportFileList(series, possibleExtraFiles);
_logger.Info("Found {0} possible extra files, imported {1} files.", possibleExtraFiles.Count, importedFiles.Count); _logger.Info("Found {0} possible extra files, imported {1} files.", possibleExtraFiles.Count, importedFiles.Count);
} }
} }

View File

@ -17,6 +17,7 @@ namespace NzbDrone.Core.Extras
{ {
public interface IExtraService public interface IExtraService
{ {
void MoveFilesAfterRename(Series series, EpisodeFile episodeFile);
void ImportEpisode(LocalEpisode localEpisode, EpisodeFile episodeFile, bool isReadOnly); void ImportEpisode(LocalEpisode localEpisode, EpisodeFile episodeFile, bool isReadOnly);
} }
@ -139,6 +140,16 @@ namespace NzbDrone.Core.Extras
} }
} }
public void MoveFilesAfterRename(Series series, EpisodeFile episodeFile)
{
var episodeFiles = new List<EpisodeFile> { episodeFile };
foreach (var extraFileManager in _extraFileManagers)
{
extraFileManager.MoveFilesAfterRename(series, episodeFiles);
}
}
public void Handle(SeriesRenamedEvent message) public void Handle(SeriesRenamedEvent message)
{ {
var series = message.Series; var series = message.Series;

View File

@ -130,6 +130,7 @@ namespace NzbDrone.Core.MediaFiles
try try
{ {
MoveEpisodeFile(episodeFile, series, episodeFile.Episodes); MoveEpisodeFile(episodeFile, series, episodeFile.Episodes);
localEpisode.ImportRenamed = true;
} }
catch (SameFilenameException) catch (SameFilenameException)
{ {

View File

@ -26,6 +26,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
private readonly IUpgradeMediaFiles _episodeFileUpgrader; private readonly IUpgradeMediaFiles _episodeFileUpgrader;
private readonly IMediaFileService _mediaFileService; private readonly IMediaFileService _mediaFileService;
private readonly IExtraService _extraService; private readonly IExtraService _extraService;
private readonly IExistingExtraFiles _existingExtraFiles;
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly IManageCommandQueue _commandQueueManager; private readonly IManageCommandQueue _commandQueueManager;
@ -34,6 +35,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
public ImportApprovedEpisodes(IUpgradeMediaFiles episodeFileUpgrader, public ImportApprovedEpisodes(IUpgradeMediaFiles episodeFileUpgrader,
IMediaFileService mediaFileService, IMediaFileService mediaFileService,
IExtraService extraService, IExtraService extraService,
IExistingExtraFiles existingExtraFiles,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IEventAggregator eventAggregator, IEventAggregator eventAggregator,
IManageCommandQueue commandQueueManager, IManageCommandQueue commandQueueManager,
@ -42,6 +44,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
_episodeFileUpgrader = episodeFileUpgrader; _episodeFileUpgrader = episodeFileUpgrader;
_mediaFileService = mediaFileService; _mediaFileService = mediaFileService;
_extraService = extraService; _extraService = extraService;
_existingExtraFiles = existingExtraFiles;
_diskProvider = diskProvider; _diskProvider = diskProvider;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_commandQueueManager = commandQueueManager; _commandQueueManager = commandQueueManager;
@ -129,9 +132,21 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
importResults.Add(new ImportResult(importDecision)); importResults.Add(new ImportResult(importDecision));
if (newDownload) if (newDownload)
{
if (localEpisode.ScriptImported)
{
_existingExtraFiles.ImportFileList(localEpisode.Series, localEpisode.PossibleExtraFiles);
if (localEpisode.ImportRenamed)
{
_extraService.MoveFilesAfterRename(localEpisode.Series, episodeFile);
}
}
else
{ {
_extraService.ImportEpisode(localEpisode, episodeFile, copyOnly); _extraService.ImportEpisode(localEpisode, episodeFile, copyOnly);
} }
}
_eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile, oldFiles, newDownload, downloadClientItem)); _eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile, oldFiles, newDownload, downloadClientItem));
} }

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -6,6 +7,7 @@ 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.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;
@ -25,6 +27,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 Logger _logger; private readonly Logger _logger;
public ImportScriptService(IProcessProvider processProvider, public ImportScriptService(IProcessProvider processProvider,
@ -32,6 +35,7 @@ namespace NzbDrone.Core.MediaFiles
IConfigService configService, IConfigService configService,
IConfigFileProvider configFileProvider, IConfigFileProvider configFileProvider,
ITagRepository tagRepository, ITagRepository tagRepository,
IExistingExtraFiles existingExtraFiles,
Logger logger) Logger logger)
{ {
_processProvider = processProvider; _processProvider = processProvider;
@ -39,9 +43,63 @@ namespace NzbDrone.Core.MediaFiles
_configService = configService; _configService = configService;
_configFileProvider = configFileProvider; _configFileProvider = configFileProvider;
_tagRepository = tagRepository; _tagRepository = tagRepository;
_existingExtraFiles = existingExtraFiles;
_logger = logger; _logger = logger;
} }
private (List<string> possibleMediaFiles, string mediaFile) ProcessTemporaryFile(string temporaryPath)
{
var lines = File.ReadAllLines(temporaryPath);
if (lines.Length == 0 || string.IsNullOrWhiteSpace(lines[0]))
{
return (new List<string>(), null);
}
var possibleExtraFiles = new List<string>();
string mediaFile = null;
foreach (var line in lines)
{
if (MediaFileExtensions.Extensions.Contains(Path.GetExtension(line)))
{
if (mediaFile is not null)
{
throw new ScriptImportException("Script output contains multiple media files. Only one media file can be returned.");
}
else
{
if (File.Exists(line))
{
mediaFile = line;
}
else
{
_logger.Warn("Script output contains invalid file: {0}", line);
}
}
}
else
{
if (File.Exists(line))
{
possibleExtraFiles.Add(line);
}
else
{
_logger.Warn("Script output contains invalid file: {0}", line);
}
}
}
if (mediaFile is not null)
{
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)
{ {
var series = localEpisode.Series; var series = localEpisode.Series;
@ -54,10 +112,13 @@ 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);
@ -118,11 +179,21 @@ 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);
localEpisode.PossibleExtraFiles = possibleExtraFiles;
destinationFilePath = mediaFile ?? destinationFilePath;
episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilePath);
episodeFile.Path = destinationFilePath;
switch (processOutput.ExitCode) switch (processOutput.ExitCode)
{ {
case 0: // Copy complete case 0: // Copy complete
localEpisode.ScriptImported = true;
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;
episodeFile.MediaInfo = _videoFileInfoReader.GetMediaInfo(destinationFilePath); episodeFile.MediaInfo = _videoFileInfoReader.GetMediaInfo(destinationFilePath);
episodeFile.Path = null; episodeFile.Path = null;
return ScriptImportDecision.RenameRequested; return ScriptImportDecision.RenameRequested;

View File

@ -40,6 +40,9 @@ namespace NzbDrone.Core.Parser.Model
public List<CustomFormat> CustomFormats { get; set; } public List<CustomFormat> CustomFormats { get; set; }
public int CustomFormatScore { get; set; } public int CustomFormatScore { get; set; }
public GrabbedReleaseInfo Release { get; set; } public GrabbedReleaseInfo Release { get; set; }
public bool ScriptImported { get; set; }
public bool ImportRenamed { get; set; }
public List<string> PossibleExtraFiles { get; set; }
public int SeasonNumber public int SeasonNumber
{ {