diff --git a/frontend/src/Settings/MediaManagement/MediaManagement.js b/frontend/src/Settings/MediaManagement/MediaManagement.js
index c9ec49e62..7bac20529 100644
--- a/frontend/src/Settings/MediaManagement/MediaManagement.js
+++ b/frontend/src/Settings/MediaManagement/MediaManagement.js
@@ -68,29 +68,29 @@ class MediaManagement extends Component {
{
- isFetching &&
+ isFetching ?
+ : null
}
{
- !isFetching && error &&
+ !isFetching && error ?
+ : null
}
{
- hasSettings && !isFetching && !error &&
+ hasSettings && !isFetching && !error ?
+ : null
}
diff --git a/src/Lidarr.Api.V1/Config/MediaManagementConfigController.cs b/src/Lidarr.Api.V1/Config/MediaManagementConfigController.cs
index 8a19c6175..729f013b9 100644
--- a/src/Lidarr.Api.V1/Config/MediaManagementConfigController.cs
+++ b/src/Lidarr.Api.V1/Config/MediaManagementConfigController.cs
@@ -32,6 +32,9 @@ namespace Lidarr.Api.V1.Config
.When(c => !string.IsNullOrWhiteSpace(c.RecycleBin));
SharedValidator.RuleFor(c => c.RecycleBinCleanupDays).GreaterThanOrEqualTo(0);
SharedValidator.RuleFor(c => c.ChmodFolder).SetValidator(folderChmodValidator).When(c => !string.IsNullOrEmpty(c.ChmodFolder) && (OsInfo.IsLinux || OsInfo.IsOsx));
+
+ SharedValidator.RuleFor(c => c.ScriptImportPath).IsValidPath().When(c => c.UseScriptImport);
+
SharedValidator.RuleFor(c => c.MinimumFreeSpaceWhenImporting).GreaterThanOrEqualTo(100);
}
diff --git a/src/Lidarr.Api.V1/Config/MediaManagementConfigResource.cs b/src/Lidarr.Api.V1/Config/MediaManagementConfigResource.cs
index e318c3c82..7779ef035 100644
--- a/src/Lidarr.Api.V1/Config/MediaManagementConfigResource.cs
+++ b/src/Lidarr.Api.V1/Config/MediaManagementConfigResource.cs
@@ -25,6 +25,8 @@ namespace Lidarr.Api.V1.Config
public bool SkipFreeSpaceCheckWhenImporting { get; set; }
public int MinimumFreeSpaceWhenImporting { get; set; }
public bool CopyUsingHardlinks { get; set; }
+ public bool UseScriptImport { get; set; }
+ public string ScriptImportPath { get; set; }
public bool ImportExtraFiles { get; set; }
public string ExtraFileExtensions { get; set; }
}
@@ -53,6 +55,8 @@ namespace Lidarr.Api.V1.Config
SkipFreeSpaceCheckWhenImporting = model.SkipFreeSpaceCheckWhenImporting,
MinimumFreeSpaceWhenImporting = model.MinimumFreeSpaceWhenImporting,
CopyUsingHardlinks = model.CopyUsingHardlinks,
+ UseScriptImport = model.UseScriptImport,
+ ScriptImportPath = model.ScriptImportPath,
ImportExtraFiles = model.ImportExtraFiles,
ExtraFileExtensions = model.ExtraFileExtensions,
};
diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs
index 8e74fcdf9..f7e195e81 100644
--- a/src/NzbDrone.Core/Configuration/ConfigService.cs
+++ b/src/NzbDrone.Core/Configuration/ConfigService.cs
@@ -200,6 +200,20 @@ namespace NzbDrone.Core.Configuration
set { SetValue("CopyUsingHardlinks", value); }
}
+ public bool UseScriptImport
+ {
+ get { return GetValueBoolean("UseScriptImport", false); }
+
+ set { SetValue("UseScriptImport", value); }
+ }
+
+ public string ScriptImportPath
+ {
+ get { return GetValue("ScriptImportPath"); }
+
+ set { SetValue("ScriptImportPath", value); }
+ }
+
public bool ImportExtraFiles
{
get { return GetValueBoolean("ImportExtraFiles", false); }
diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs
index 6cef2ad88..674249ab7 100644
--- a/src/NzbDrone.Core/Configuration/IConfigService.cs
+++ b/src/NzbDrone.Core/Configuration/IConfigService.cs
@@ -32,6 +32,8 @@ namespace NzbDrone.Core.Configuration
bool SkipFreeSpaceCheckWhenImporting { get; set; }
int MinimumFreeSpaceWhenImporting { get; set; }
bool CopyUsingHardlinks { get; set; }
+ bool UseScriptImport { get; set; }
+ string ScriptImportPath { get; set; }
bool ImportExtraFiles { get; set; }
string ExtraFileExtensions { get; set; }
bool WatchLibraryForChanges { get; set; }
diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json
index 4ea0c4962..f5c602dce 100644
--- a/src/NzbDrone.Core/Localization/Core/en.json
+++ b/src/NzbDrone.Core/Localization/Core/en.json
@@ -392,6 +392,8 @@
"ImportListStatusCheckSingleClientMessage": "Lists unavailable due to failures: {0}",
"ImportLists": "Import Lists",
"ImportMechanismHealthCheckMessage": "Enable Completed Download Handling",
+ "ImportScriptPath": "Import Script Path",
+ "ImportUsingScript": "Import Using Script",
"ImportedTo": "Imported To",
"Importing": "Importing",
"Inactive": "Inactive",
@@ -754,6 +756,7 @@
"SceneInformation": "Scene Information",
"SceneNumberHasntBeenVerifiedYet": "Scene number hasn't been verified yet",
"Scheduled": "Scheduled",
+ "ScriptImportPathHelpText": "The path to the script to use for importing",
"ScriptPath": "Script Path",
"ScrubAudioTagsHelpText": "Remove existing tags from files, leaving only those added by Lidarr.",
"ScrubExistingTags": "Scrub Existing Tags",
@@ -963,6 +966,7 @@
"UrlBaseHelpTextWarning": "Requires restart to take effect",
"UseHardlinksInsteadOfCopy": "Use Hardlinks instead of Copy",
"UseProxy": "Use Proxy",
+ "UseScriptImportHelpText": "Copy files for importing using a script (ex. for transcoding)",
"Usenet": "Usenet",
"UsenetDelay": "Usenet Delay",
"UsenetDelayHelpText": "Delay in minutes to wait before grabbing a release from Usenet",
diff --git a/src/NzbDrone.Core/MediaFiles/ScriptImportDecider.cs b/src/NzbDrone.Core/MediaFiles/ScriptImportDecider.cs
new file mode 100644
index 000000000..043d0ec3b
--- /dev/null
+++ b/src/NzbDrone.Core/MediaFiles/ScriptImportDecider.cs
@@ -0,0 +1,100 @@
+using System.Collections.Specialized;
+using System.Linq;
+using NLog;
+using NzbDrone.Common.Disk;
+using NzbDrone.Common.Processes;
+using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Parser.Model;
+
+namespace NzbDrone.Core.MediaFiles
+{
+ public interface IImportScript
+ {
+ public ScriptImportDecision TryImport(string sourcePath, string destinationFilePath, LocalTrack localTrack, TrackFile trackFile, TransferMode mode);
+ }
+
+ public class ImportScriptService : IImportScript
+ {
+ private readonly IConfigFileProvider _configFileProvider;
+ private readonly IProcessProvider _processProvider;
+ private readonly IConfigService _configService;
+ private readonly Logger _logger;
+
+ public ImportScriptService(IProcessProvider processProvider,
+ IConfigService configService,
+ IConfigFileProvider configFileProvider,
+ Logger logger)
+ {
+ _processProvider = processProvider;
+ _configService = configService;
+ _configFileProvider = configFileProvider;
+ _logger = logger;
+ }
+
+ public ScriptImportDecision TryImport(string sourcePath, string destinationFilePath, LocalTrack localTrack, TrackFile trackFile, TransferMode mode)
+ {
+ var artist = localTrack.Artist;
+ var oldFiles = localTrack.OldFiles;
+ var downloadClientInfo = localTrack.DownloadItem?.DownloadClientInfo;
+ var downloadId = localTrack.DownloadItem?.DownloadId;
+
+ if (!_configService.UseScriptImport)
+ {
+ return ScriptImportDecision.DeferMove;
+ }
+
+ var environmentVariables = new StringDictionary();
+
+ environmentVariables.Add("Lidarr_SourcePath", sourcePath);
+ environmentVariables.Add("Lidarr_DestinationPath", destinationFilePath);
+
+ environmentVariables.Add("Lidarr_InstanceName", _configFileProvider.InstanceName);
+ environmentVariables.Add("Lidarr_ApplicationUrl", _configService.ApplicationUrl);
+ environmentVariables.Add("Lidarr_TransferMode", mode.ToString());
+
+ environmentVariables.Add("Lidarr_Artist_Id", artist.Id.ToString());
+ environmentVariables.Add("Lidarr_Artist_Name", artist.Metadata.Value.Name);
+ environmentVariables.Add("Lidarr_Artist_Path", artist.Path);
+ environmentVariables.Add("Lidarr_Artist_MBId", artist.Metadata.Value.ForeignArtistId);
+ environmentVariables.Add("Lidarr_Artist_Type", artist.Metadata.Value.Type);
+
+ environmentVariables.Add("Lidarr_Album_Id", localTrack.Album.Id.ToString());
+ environmentVariables.Add("Lidarr_Album_Title", localTrack.Album.Title);
+ environmentVariables.Add("Lidarr_Album_Overview", localTrack.Album.Overview);
+ environmentVariables.Add("Lidarr_Album_MBId", localTrack.Album.ForeignAlbumId);
+ environmentVariables.Add("Lidarr_AlbumRelease_MBId", localTrack.Release.ForeignReleaseId);
+ environmentVariables.Add("Lidarr_Album_ReleaseDate", localTrack.Release.ReleaseDate.ToString());
+
+ environmentVariables.Add("Lidarr_Download_Client", downloadClientInfo?.Name ?? string.Empty);
+ environmentVariables.Add("Lidarr_Download_Client_Type", downloadClientInfo?.Type ?? string.Empty);
+ environmentVariables.Add("Lidarr_Download_Id", downloadId ?? string.Empty);
+
+ if (oldFiles.Any())
+ {
+ environmentVariables.Add("Lidarr_DeletedPaths", string.Join("|", oldFiles.Select(e => e.Path)));
+ environmentVariables.Add("Lidarr_DeletedDateAdded", string.Join("|", oldFiles.Select(e => e.DateAdded)));
+ }
+
+ _logger.Debug("Executing external script: {0}", _configService.ScriptImportPath);
+
+ var processOutput = _processProvider.StartAndCapture(_configService.ScriptImportPath, $"\"{sourcePath}\" \"{destinationFilePath}\"", environmentVariables);
+
+ _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));
+
+ switch (processOutput.ExitCode)
+ {
+ case 0: // Copy complete
+ return ScriptImportDecision.MoveComplete;
+ case 2: // Copy complete, file potentially changed, should try renaming again
+ // trackFile.MediaInfo = _videoFileInfoReader.GetMediaInfo(destinationFilePath);
+ trackFile.Path = null;
+ return ScriptImportDecision.RenameRequested;
+ case 3: // Let Lidarr handle it
+ return ScriptImportDecision.DeferMove;
+ default: // Error, fail to import
+ throw new ScriptImportException("Moving with script failed! Exit code {0}", processOutput.ExitCode);
+ }
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/MediaFiles/ScriptImportDecision.cs b/src/NzbDrone.Core/MediaFiles/ScriptImportDecision.cs
new file mode 100644
index 000000000..fb2eb4f6f
--- /dev/null
+++ b/src/NzbDrone.Core/MediaFiles/ScriptImportDecision.cs
@@ -0,0 +1,10 @@
+namespace NzbDrone.Core.MediaFiles
+{
+ public enum ScriptImportDecision
+ {
+ MoveComplete,
+ RenameRequested,
+ RejectExtra,
+ DeferMove
+ }
+}
diff --git a/src/NzbDrone.Core/MediaFiles/ScriptImportException.cs b/src/NzbDrone.Core/MediaFiles/ScriptImportException.cs
new file mode 100644
index 000000000..9ac0f49d4
--- /dev/null
+++ b/src/NzbDrone.Core/MediaFiles/ScriptImportException.cs
@@ -0,0 +1,23 @@
+using System;
+using NzbDrone.Common.Exceptions;
+
+namespace NzbDrone.Core.MediaFiles
+{
+ public class ScriptImportException : NzbDroneException
+ {
+ public ScriptImportException(string message)
+ : base(message)
+ {
+ }
+
+ public ScriptImportException(string message, params object[] args)
+ : base(message, args)
+ {
+ }
+
+ public ScriptImportException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs b/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs
index 95519482f..44266e888 100644
--- a/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs
+++ b/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs
@@ -31,6 +31,7 @@ namespace NzbDrone.Core.MediaFiles
private readonly IDiskProvider _diskProvider;
private readonly IRootFolderWatchingService _rootFolderWatchingService;
private readonly IMediaFileAttributeService _mediaFileAttributeService;
+ private readonly IImportScript _scriptImportDecider;
private readonly IEventAggregator _eventAggregator;
private readonly IConfigService _configService;
private readonly Logger _logger;
@@ -43,6 +44,7 @@ namespace NzbDrone.Core.MediaFiles
IDiskProvider diskProvider,
IRootFolderWatchingService rootFolderWatchingService,
IMediaFileAttributeService mediaFileAttributeService,
+ IImportScript scriptImportDecider,
IEventAggregator eventAggregator,
IConfigService configService,
Logger logger)
@@ -55,6 +57,7 @@ namespace NzbDrone.Core.MediaFiles
_diskProvider = diskProvider;
_rootFolderWatchingService = rootFolderWatchingService;
_mediaFileAttributeService = mediaFileAttributeService;
+ _scriptImportDecider = scriptImportDecider;
_eventAggregator = eventAggregator;
_configService = configService;
_logger = logger;
@@ -63,6 +66,11 @@ namespace NzbDrone.Core.MediaFiles
public TrackFile MoveTrackFile(TrackFile trackFile, Artist artist)
{
var tracks = _trackService.GetTracksByFileId(trackFile.Id);
+ return MoveTrackFile(trackFile, artist, tracks);
+ }
+
+ private TrackFile MoveTrackFile(TrackFile trackFile, Artist artist, List