New: Reprocess items after selection in Manual Import

Fixes #5199

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
This commit is contained in:
Qstick 2021-01-31 01:02:51 -05:00
parent f33f004aa9
commit 2a93686360
7 changed files with 77 additions and 29 deletions

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
import { reprocessInteractiveImportItems, updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
import { fetchLanguages } from 'Store/Actions/settingsActions';
import SelectLanguageModalContent from './SelectLanguageModalContent';
@ -33,6 +33,7 @@ function createMapStateToProps() {
const mapDispatchToProps = {
dispatchFetchLanguages: fetchLanguages,
dispatchReprocessInteractiveImportItems: reprocessInteractiveImportItems,
dispatchUpdateInteractiveImportItems: updateInteractiveImportItems
};
@ -51,6 +52,12 @@ class SelectLanguageModalContentConnector extends Component {
// Listeners
onLanguageSelect = ({ languageIds }) => {
const {
ids,
dispatchUpdateInteractiveImportItems,
dispatchReprocessInteractiveImportItems
} = this.props;
const languages = [];
languageIds.forEach((languageId) => {
@ -62,11 +69,13 @@ class SelectLanguageModalContentConnector extends Component {
}
});
this.props.dispatchUpdateInteractiveImportItems({
ids: this.props.ids,
dispatchUpdateInteractiveImportItems({
ids,
languages
});
dispatchReprocessInteractiveImportItems({ ids });
this.props.onModalClose(true);
}
@ -91,6 +100,7 @@ SelectLanguageModalContentConnector.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired,
dispatchFetchLanguages: PropTypes.func.isRequired,
dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
dispatchReprocessInteractiveImportItems: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
import { reprocessInteractiveImportItems, updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
import { fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
import getQualities from 'Utilities/Quality/getQualities';
import SelectQualityModalContent from './SelectQualityModalContent';
@ -31,6 +31,7 @@ function createMapStateToProps() {
const mapDispatchToProps = {
dispatchFetchQualityProfileSchema: fetchQualityProfileSchema,
dispatchReprocessInteractiveImportItems: reprocessInteractiveImportItems,
dispatchUpdateInteractiveImportItems: updateInteractiveImportItems
};
@ -49,6 +50,12 @@ class SelectQualityModalContentConnector extends Component {
// Listeners
onQualitySelect = ({ qualityId, proper, real }) => {
const {
ids,
dispatchUpdateInteractiveImportItems,
dispatchReprocessInteractiveImportItems
} = this.props;
const quality = _.find(this.props.items,
(item) => item.id === qualityId);
@ -57,14 +64,16 @@ class SelectQualityModalContentConnector extends Component {
real: real ? 1 : 0
};
this.props.dispatchUpdateInteractiveImportItems({
ids: this.props.ids,
dispatchUpdateInteractiveImportItems({
ids,
quality: {
quality,
revision
}
});
dispatchReprocessInteractiveImportItems({ ids });
this.props.onModalClose(true);
}
@ -88,6 +97,7 @@ SelectQualityModalContentConnector.propTypes = {
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
dispatchFetchQualityProfileSchema: PropTypes.func.isRequired,
dispatchReprocessInteractiveImportItems: PropTypes.func.isRequired,
dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};

View File

@ -149,6 +149,8 @@ export const actionHandlers = handleThunks({
id,
path: item.path,
movieId: item.movie.id,
quality: item.quality,
languages: item.languages,
downloadId: item.downloadId
};
});

View File

@ -18,6 +18,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie);
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);
}
public class ImportDecisionMaker : IMakeImportDecision
@ -93,6 +94,14 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
return decisions;
}
public ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem downloadClientItem)
{
var reasons = _specifications.Select(c => EvaluateSpec(c, localMovie, downloadClientItem))
.Where(c => c != null);
return new ImportDecision(localMovie, reasons.ToArray());
}
private ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
{
ImportDecision decision = null;
@ -147,14 +156,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
return decision;
}
private ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem downloadClientItem)
{
var reasons = _specifications.Select(c => EvaluateSpec(c, localMovie, downloadClientItem))
.Where(c => c != null);
return new ImportDecision(localMovie, reasons.ToArray());
}
private Rejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalMovie localMovie, DownloadClientItem downloadClientItem)
{
try

View File

@ -8,19 +8,21 @@ using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
{
public interface IManualImportService
{
List<ManualImportItem> GetMediaFiles(string path, string downloadId, int? movieId, bool filterExistingFiles);
ManualImportItem ReprocessItem(string path, string downloadId, int movieId);
ManualImportItem ReprocessItem(string path, string downloadId, int movieId, QualityModel quality, List<Language> languages);
}
public class ManualImportService : IExecute<ManualImportCommand>, IManualImportService
@ -90,12 +92,27 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
return ProcessFolder(path, path, downloadId, movieId, filterExistingFiles);
}
public ManualImportItem ReprocessItem(string path, string downloadId, int movieId)
public ManualImportItem ReprocessItem(string path, string downloadId, int movieId, QualityModel quality, List<Language> languages)
{
var rootFolder = Path.GetDirectoryName(path);
var movie = _movieService.GetMovie(movieId);
return ProcessFile(rootFolder, rootFolder, path, downloadId, movie);
var downloadClientItem = GetTrackedDownload(downloadId)?.DownloadItem;
var localEpisode = new LocalMovie
{
Movie = movie,
FileMovieInfo = Parser.Parser.ParseMoviePath(path),
DownloadClientMovieInfo = downloadClientItem == null ? null : Parser.Parser.ParseMovieTitle(downloadClientItem.Title),
Path = path,
SceneSource = SceneSource(movie, rootFolder),
ExistingFile = movie.Path.IsParentPath(path),
Size = _diskProvider.GetFileSize(path),
Languages = languages,
Quality = quality
};
return MapItem(_importDecisionMaker.GetDecision(localEpisode, downloadClientItem), rootFolder, downloadId, null);
}
private List<ManualImportItem> ProcessFolder(string rootFolder, string baseFolder, string downloadId, int? movieId, bool filterExistingFiles)
@ -141,7 +158,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
private ManualImportItem ProcessFile(string rootFolder, string baseFolder, string file, string downloadId, Movie movie = null)
{
DownloadClientItem downloadClientItem = null;
var trackedDownload = GetTrackedDownload(downloadId);
var relativeFile = baseFolder.GetRelativePath(file);
if (movie == null)
@ -154,15 +171,9 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
movie = _parsingService.GetMovie(relativeFile);
}
if (downloadId.IsNotNullOrWhiteSpace())
if (trackedDownload != null && movie == null)
{
var trackedDownload = _trackedDownloadService.Find(downloadId);
downloadClientItem = trackedDownload?.DownloadItem;
if (movie == null)
{
movie = trackedDownload?.RemoteMovie?.Movie;
}
movie = trackedDownload?.RemoteMovie?.Movie;
}
if (movie == null)
@ -186,7 +197,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), rootFolder, downloadId, null);
}
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file }, movie, downloadClientItem, null, SceneSource(movie, baseFolder));
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file }, movie, trackedDownload?.DownloadItem, null, SceneSource(movie, baseFolder));
if (importDecisions.Any())
{
@ -208,6 +219,18 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
return !(movie.Path.PathEquals(folder) || movie.Path.IsParentPath(folder));
}
private TrackedDownload GetTrackedDownload(string downloadId)
{
if (downloadId.IsNotNullOrWhiteSpace())
{
var trackedDownload = _trackedDownloadService.Find(downloadId);
return trackedDownload;
}
return null;
}
private ManualImportItem MapItem(ImportDecision decision, string rootFolder, string downloadId, string folderName)
{
var item = new ManualImportItem();

View File

@ -37,7 +37,7 @@ namespace Radarr.Api.V3.ManualImport
foreach (var item in items)
{
var processedItem = _manualImportService.ReprocessItem(item.Path, item.DownloadId, item.MovieId);
var processedItem = _manualImportService.ReprocessItem(item.Path, item.DownloadId, item.MovieId, item.Quality, item.Languages);
item.Movie = processedItem.Movie.ToResource(0);
item.Rejections = processedItem.Rejections;

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Qualities;
using Radarr.Api.V3.Movies;
using Radarr.Http.REST;
@ -11,9 +12,10 @@ namespace Radarr.Api.V3.ManualImport
public string Path { get; set; }
public int MovieId { get; set; }
public MovieResource Movie { get; set; }
public QualityModel Quality { get; set; }
public List<Language> Languages { get; set; }
public string DownloadId { get; set; }
public IEnumerable<Rejection> Rejections { get; set; }
public List<Language> Languages { get; set; }
}
}