New: Reprocess Language in Manual Import after Movie Selection

pull/4819/head
Qstick 4 years ago
parent 9c85482b9c
commit e4d033d0a7

@ -14,3 +14,10 @@
cursor: pointer; cursor: pointer;
} }
.reprocessing {
composes: loading from '~Components/Loading/LoadingIndicator.css';
margin-top: 0;
text-align: start;
}

@ -1,6 +1,7 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRowCellButton from 'Components/Table/Cells/TableRowCellButton'; import TableRowCellButton from 'Components/Table/Cells/TableRowCellButton';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell'; import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
@ -137,6 +138,7 @@ class InteractiveImportRow extends Component {
languages, languages,
size, size,
rejections, rejections,
isReprocessing,
isSelected, isSelected,
onSelectedChange onSelectedChange
} = this.props; } = this.props;
@ -151,7 +153,7 @@ class InteractiveImportRow extends Component {
const showMoviePlaceholder = isSelected && !movie; const showMoviePlaceholder = isSelected && !movie;
const showQualityPlaceholder = isSelected && !quality; const showQualityPlaceholder = isSelected && !quality;
const showLanguagePlaceholder = isSelected && !languages; const showLanguagePlaceholder = isSelected && !languages && !isReprocessing;
return ( return (
<TableRow> <TableRow>
@ -208,11 +210,20 @@ class InteractiveImportRow extends Component {
} }
{ {
!showLanguagePlaceholder && !!languages && !showLanguagePlaceholder && !!languages && !isReprocessing ?
<MovieLanguage <MovieLanguage
className={styles.label} className={styles.label}
languages={languages} languages={languages}
/> /> :
null
}
{
isReprocessing ?
<LoadingIndicator className={styles.reprocessing}
size={20}
/> : null
} }
</TableRowCellButton> </TableRowCellButton>
@ -286,6 +297,7 @@ InteractiveImportRow.propTypes = {
languages: PropTypes.arrayOf(PropTypes.object), languages: PropTypes.arrayOf(PropTypes.object),
size: PropTypes.number.isRequired, size: PropTypes.number.isRequired,
rejections: PropTypes.arrayOf(PropTypes.object).isRequired, rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
isReprocessing: PropTypes.bool,
isSelected: PropTypes.bool, isSelected: PropTypes.bool,
onSelectedChange: PropTypes.func.isRequired, onSelectedChange: PropTypes.func.isRequired,
onValidRowChange: PropTypes.func.isRequired onValidRowChange: PropTypes.func.isRequired

@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions'; import { reprocessInteractiveImportItems, updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector'; import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector';
import createDeepEqualSelector from 'Store/Selectors/createDeepEqualSelector'; import createDeepEqualSelector from 'Store/Selectors/createDeepEqualSelector';
import SelectMovieModalContent from './SelectMovieModalContent'; import SelectMovieModalContent from './SelectMovieModalContent';
@ -59,6 +59,7 @@ function createMapStateToProps() {
} }
const mapDispatchToProps = { const mapDispatchToProps = {
dispatchReprocessInteractiveImportItems: reprocessInteractiveImportItems,
dispatchUpdateInteractiveImportItem: updateInteractiveImportItem dispatchUpdateInteractiveImportItem: updateInteractiveImportItem
}; };
@ -72,6 +73,7 @@ class SelectMovieModalContentConnector extends Component {
ids, ids,
items, items,
dispatchUpdateInteractiveImportItem, dispatchUpdateInteractiveImportItem,
dispatchReprocessInteractiveImportItems,
onModalClose onModalClose
} = this.props; } = this.props;
@ -84,6 +86,8 @@ class SelectMovieModalContentConnector extends Component {
}); });
}); });
dispatchReprocessInteractiveImportItems({ ids });
onModalClose(true); onModalClose(true);
} }
@ -103,6 +107,7 @@ class SelectMovieModalContentConnector extends Component {
SelectMovieModalContentConnector.propTypes = { SelectMovieModalContentConnector.propTypes = {
ids: PropTypes.arrayOf(PropTypes.number).isRequired, ids: PropTypes.arrayOf(PropTypes.number).isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired,
dispatchReprocessInteractiveImportItems: PropTypes.func.isRequired,
dispatchUpdateInteractiveImportItem: PropTypes.func.isRequired, dispatchUpdateInteractiveImportItem: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };

@ -4,7 +4,7 @@ import { batchActions } from 'redux-batched-actions';
import { sortDirections } from 'Helpers/Props'; import { sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks'; import { createThunk, handleThunks } from 'Store/thunks';
import createAjaxRequest from 'Utilities/createAjaxRequest'; import createAjaxRequest from 'Utilities/createAjaxRequest';
import { set, update } from './baseActions'; import { set, update, updateItem } from './baseActions';
import createHandleActions from './Creators/createHandleActions'; import createHandleActions from './Creators/createHandleActions';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer'; import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
@ -15,12 +15,16 @@ export const section = 'interactiveImport';
const MAXIMUM_RECENT_FOLDERS = 10; const MAXIMUM_RECENT_FOLDERS = 10;
let abortCurrentRequest = null;
let currentIds = [];
// //
// State // State
export const defaultState = { export const defaultState = {
isFetching: false, isFetching: false,
isPopulated: false, isPopulated: false,
isReprocessing: false,
error: null, error: null,
items: [], items: [],
sortKey: 'quality', sortKey: 'quality',
@ -55,6 +59,7 @@ export const persistState = [
// Actions Types // Actions Types
export const FETCH_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/fetchInteractiveImportItems'; export const FETCH_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/fetchInteractiveImportItems';
export const REPROCESS_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/reprocessInteractiveImportItems';
export const SET_INTERACTIVE_IMPORT_SORT = 'interactiveImport/setInteractiveImportSort'; export const SET_INTERACTIVE_IMPORT_SORT = 'interactiveImport/setInteractiveImportSort';
export const UPDATE_INTERACTIVE_IMPORT_ITEM = 'interactiveImport/updateInteractiveImportItem'; export const UPDATE_INTERACTIVE_IMPORT_ITEM = 'interactiveImport/updateInteractiveImportItem';
export const UPDATE_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/updateInteractiveImportItems'; export const UPDATE_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/updateInteractiveImportItems';
@ -67,6 +72,7 @@ export const SET_INTERACTIVE_IMPORT_MODE = 'interactiveImport/setInteractiveImpo
// Action Creators // Action Creators
export const fetchInteractiveImportItems = createThunk(FETCH_INTERACTIVE_IMPORT_ITEMS); export const fetchInteractiveImportItems = createThunk(FETCH_INTERACTIVE_IMPORT_ITEMS);
export const reprocessInteractiveImportItems = createThunk(REPROCESS_INTERACTIVE_IMPORT_ITEMS);
export const setInteractiveImportSort = createAction(SET_INTERACTIVE_IMPORT_SORT); export const setInteractiveImportSort = createAction(SET_INTERACTIVE_IMPORT_SORT);
export const updateInteractiveImportItem = createAction(UPDATE_INTERACTIVE_IMPORT_ITEM); export const updateInteractiveImportItem = createAction(UPDATE_INTERACTIVE_IMPORT_ITEM);
export const updateInteractiveImportItems = createAction(UPDATE_INTERACTIVE_IMPORT_ITEMS); export const updateInteractiveImportItems = createAction(UPDATE_INTERACTIVE_IMPORT_ITEMS);
@ -112,6 +118,72 @@ export const actionHandlers = handleThunks({
error: xhr error: xhr
})); }));
}); });
},
[REPROCESS_INTERACTIVE_IMPORT_ITEMS]: function(getState, payload, dispatch) {
if (abortCurrentRequest) {
abortCurrentRequest();
}
dispatch(batchActions([
...currentIds.map((id) => updateItem({
section,
id,
isReprocessing: false
})),
...payload.ids.map((id) => updateItem({
section,
id,
isReprocessing: true
}))
]));
const items = getState()[section].items;
const requestPayload = payload.ids.map((id) => {
const item = items.find((i) => i.id === id);
return {
id,
path: item.path,
movieId: item.movie.id,
downloadId: item.downloadId
};
});
const { request, abortRequest } = createAjaxRequest({
method: 'POST',
url: '/manualimport',
contentType: 'application/json',
data: JSON.stringify(requestPayload)
});
abortCurrentRequest = abortRequest;
currentIds = payload.ids;
request.done((data) => {
dispatch(batchActions(
data.map((item) => updateItem({
section,
...item,
isReprocessing: false
}))
));
});
request.fail((xhr) => {
if (xhr.aborted) {
return;
}
dispatch(batchActions(
payload.ids.map((id) => updateItem({
section,
id,
isReprocessing: false
}))
));
});
} }
}); });

@ -5,11 +5,9 @@ using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.History;
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation; using NzbDrone.Core.MediaFiles.MovieImport.Aggregation;
using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
@ -22,6 +20,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
public interface IManualImportService public interface IManualImportService
{ {
List<ManualImportItem> GetMediaFiles(string path, string downloadId, int? movieId, bool filterExistingFiles); List<ManualImportItem> GetMediaFiles(string path, string downloadId, int? movieId, bool filterExistingFiles);
ManualImportItem ReprocessItem(string path, string downloadId, int movieId);
} }
public class ManualImportService : IExecute<ManualImportCommand>, IManualImportService public class ManualImportService : IExecute<ManualImportCommand>, IManualImportService
@ -36,8 +35,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
private readonly ITrackedDownloadService _trackedDownloadService; private readonly ITrackedDownloadService _trackedDownloadService;
private readonly IDownloadedMovieImportService _downloadedMovieImportService; private readonly IDownloadedMovieImportService _downloadedMovieImportService;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly IConfigService _config;
private readonly IHistoryService _historyService;
private readonly Logger _logger; private readonly Logger _logger;
public ManualImportService(IDiskProvider diskProvider, public ManualImportService(IDiskProvider diskProvider,
@ -50,8 +47,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
ITrackedDownloadService trackedDownloadService, ITrackedDownloadService trackedDownloadService,
IDownloadedMovieImportService downloadedMovieImportService, IDownloadedMovieImportService downloadedMovieImportService,
IEventAggregator eventAggregator, IEventAggregator eventAggregator,
IConfigService config,
IHistoryService historyService,
Logger logger) Logger logger)
{ {
_diskProvider = diskProvider; _diskProvider = diskProvider;
@ -64,8 +59,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
_trackedDownloadService = trackedDownloadService; _trackedDownloadService = trackedDownloadService;
_downloadedMovieImportService = downloadedMovieImportService; _downloadedMovieImportService = downloadedMovieImportService;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_config = config;
_historyService = historyService;
_logger = logger; _logger = logger;
} }
@ -97,6 +90,14 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
return ProcessFolder(path, path, downloadId, movieId, filterExistingFiles); return ProcessFolder(path, path, downloadId, movieId, filterExistingFiles);
} }
public ManualImportItem ReprocessItem(string path, string downloadId, int movieId)
{
var rootFolder = Path.GetDirectoryName(path);
var movie = _movieService.GetMovie(movieId);
return ProcessFile(rootFolder, rootFolder, path, downloadId, movie);
}
private List<ManualImportItem> ProcessFolder(string rootFolder, string baseFolder, string downloadId, int? movieId, bool filterExistingFiles) private List<ManualImportItem> ProcessFolder(string rootFolder, string baseFolder, string downloadId, int? movieId, bool filterExistingFiles)
{ {
DownloadClientItem downloadClientItem = null; DownloadClientItem downloadClientItem = null;

@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.MediaFiles.MovieImport.Manual; using NzbDrone.Core.MediaFiles.MovieImport.Manual;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using Radarr.Api.V3.Movies;
using Radarr.Http; using Radarr.Http;
using Radarr.Http.Extensions; using Radarr.Http.Extensions;
@ -17,6 +18,7 @@ namespace Radarr.Api.V3.ManualImport
_manualImportService = manualImportService; _manualImportService = manualImportService;
GetResourceAll = GetMediaFiles; GetResourceAll = GetMediaFiles;
Post("/", x => ReprocessItems());
} }
private List<ManualImportResource> GetMediaFiles() private List<ManualImportResource> GetMediaFiles()
@ -29,6 +31,22 @@ namespace Radarr.Api.V3.ManualImport
return _manualImportService.GetMediaFiles(folder, downloadId, movieId, filterExistingFiles).ToResource().Select(AddQualityWeight).ToList(); return _manualImportService.GetMediaFiles(folder, downloadId, movieId, filterExistingFiles).ToResource().Select(AddQualityWeight).ToList();
} }
private object ReprocessItems()
{
var items = Request.Body.FromJson<List<ManualImportReprocessResource>>();
foreach (var item in items)
{
var processedItem = _manualImportService.ReprocessItem(item.Path, item.DownloadId, item.MovieId);
item.Movie = processedItem.Movie.ToResource();
item.Rejections = processedItem.Rejections;
item.Languages = processedItem.Languages;
}
return items;
}
private ManualImportResource AddQualityWeight(ManualImportResource item) private ManualImportResource AddQualityWeight(ManualImportResource item)
{ {
if (item.Quality != null) if (item.Quality != null)

@ -0,0 +1,19 @@
using System.Collections.Generic;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Languages;
using Radarr.Api.V3.Movies;
using Radarr.Http.REST;
namespace Radarr.Api.V3.ManualImport
{
public class ManualImportReprocessResource : RestResource
{
public string Path { get; set; }
public int MovieId { get; set; }
public MovieResource Movie { get; set; }
public string DownloadId { get; set; }
public IEnumerable<Rejection> Rejections { get; set; }
public List<Language> Languages { get; set; }
}
}

@ -7,7 +7,7 @@ using NzbDrone.Core.NetImport.ImportExclusions;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using Radarr.Http; using Radarr.Http;
namespace NzbDrone.Api.V3.Movies namespace Radarr.Api.V3.Movies
{ {
public class DiscoverMoviesModule : RadarrRestModule<DiscoverMoviesResource> public class DiscoverMoviesModule : RadarrRestModule<DiscoverMoviesResource>
{ {

@ -5,7 +5,7 @@ using NzbDrone.Core.Movies;
using NzbDrone.Core.NetImport.ImportExclusions; using NzbDrone.Core.NetImport.ImportExclusions;
using Radarr.Http.REST; using Radarr.Http.REST;
namespace NzbDrone.Api.V3.Movies namespace Radarr.Api.V3.Movies
{ {
public class DiscoverMoviesResource : RestResource public class DiscoverMoviesResource : RestResource
{ {

Loading…
Cancel
Save