Manual Import: Reprocess after selecting series
New: Reprocess changed items when series is changed Closes #1893
This commit is contained in:
parent
88ecec2f9a
commit
9ad3b12403
|
@ -19,11 +19,11 @@ function createMapStateToProps() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchInteractiveImportItems,
|
dispatchFetchInteractiveImportItems: fetchInteractiveImportItems,
|
||||||
setInteractiveImportSort,
|
dispatchSetInteractiveImportSort: setInteractiveImportSort,
|
||||||
setInteractiveImportMode,
|
dispatchSetInteractiveImportMode: setInteractiveImportMode,
|
||||||
clearInteractiveImport,
|
dispatchClearInteractiveImport: clearInteractiveImport,
|
||||||
executeCommand
|
dispatchExecuteCommand: executeCommand
|
||||||
};
|
};
|
||||||
|
|
||||||
class InteractiveImportModalContentConnector extends Component {
|
class InteractiveImportModalContentConnector extends Component {
|
||||||
|
@ -50,7 +50,7 @@ class InteractiveImportModalContentConnector extends Component {
|
||||||
filterExistingFiles
|
filterExistingFiles
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
this.props.fetchInteractiveImportItems({
|
this.props.dispatchFetchInteractiveImportItems({
|
||||||
downloadId,
|
downloadId,
|
||||||
folder,
|
folder,
|
||||||
filterExistingFiles
|
filterExistingFiles
|
||||||
|
@ -68,7 +68,7 @@ class InteractiveImportModalContentConnector extends Component {
|
||||||
folder
|
folder
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.props.fetchInteractiveImportItems({
|
this.props.dispatchFetchInteractiveImportItems({
|
||||||
downloadId,
|
downloadId,
|
||||||
folder,
|
folder,
|
||||||
filterExistingFiles
|
filterExistingFiles
|
||||||
|
@ -77,14 +77,14 @@ class InteractiveImportModalContentConnector extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.props.clearInteractiveImport();
|
this.props.dispatchClearInteractiveImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onSortPress = (sortKey, sortDirection) => {
|
onSortPress = (sortKey, sortDirection) => {
|
||||||
this.props.setInteractiveImportSort({ sortKey, sortDirection });
|
this.props.dispatchSetInteractiveImportSort({ sortKey, sortDirection });
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilterExistingFilesChange = (filterExistingFiles) => {
|
onFilterExistingFilesChange = (filterExistingFiles) => {
|
||||||
|
@ -92,7 +92,7 @@ class InteractiveImportModalContentConnector extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onImportModeChange = (importMode) => {
|
onImportModeChange = (importMode) => {
|
||||||
this.props.setInteractiveImportMode({ importMode });
|
this.props.dispatchSetInteractiveImportMode({ importMode });
|
||||||
}
|
}
|
||||||
|
|
||||||
onImportSelectedPress = (selected, importMode) => {
|
onImportSelectedPress = (selected, importMode) => {
|
||||||
|
@ -139,7 +139,7 @@ class InteractiveImportModalContentConnector extends Component {
|
||||||
path: item.path,
|
path: item.path,
|
||||||
folderName: item.folderName,
|
folderName: item.folderName,
|
||||||
seriesId: series.id,
|
seriesId: series.id,
|
||||||
episodeIds: _.map(episodes, 'id'),
|
episodeIds: episodes.map((e) => e.id),
|
||||||
quality,
|
quality,
|
||||||
language,
|
language,
|
||||||
downloadId: this.props.downloadId
|
downloadId: this.props.downloadId
|
||||||
|
@ -151,7 +151,7 @@ class InteractiveImportModalContentConnector extends Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.executeCommand({
|
this.props.dispatchExecuteCommand({
|
||||||
name: commandNames.INTERACTIVE_IMPORT,
|
name: commandNames.INTERACTIVE_IMPORT,
|
||||||
files,
|
files,
|
||||||
importMode
|
importMode
|
||||||
|
@ -188,11 +188,11 @@ InteractiveImportModalContentConnector.propTypes = {
|
||||||
folder: PropTypes.string,
|
folder: PropTypes.string,
|
||||||
filterExistingFiles: PropTypes.bool.isRequired,
|
filterExistingFiles: PropTypes.bool.isRequired,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
fetchInteractiveImportItems: PropTypes.func.isRequired,
|
dispatchFetchInteractiveImportItems: PropTypes.func.isRequired,
|
||||||
setInteractiveImportSort: PropTypes.func.isRequired,
|
dispatchSetInteractiveImportSort: PropTypes.func.isRequired,
|
||||||
clearInteractiveImport: PropTypes.func.isRequired,
|
dispatchSetInteractiveImportMode: PropTypes.func.isRequired,
|
||||||
setInteractiveImportMode: PropTypes.func.isRequired,
|
dispatchClearInteractiveImport: PropTypes.func.isRequired,
|
||||||
executeCommand: PropTypes.func.isRequired,
|
dispatchExecuteCommand: PropTypes.func.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
.relativePath {
|
.relativePath {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from "~Components/Table/Cells/TableRowCell.css";
|
||||||
|
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quality,
|
.quality,
|
||||||
.language {
|
.language {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from "~Components/Table/Cells/TableRowCell.css";
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
composes: label from '~Components/Label.css';
|
composes: label from "~Components/Label.css";
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reprocessing {
|
||||||
|
composes: loading from "~Components/Loading/LoadingIndicator.css";
|
||||||
|
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
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';
|
||||||
|
@ -172,6 +173,7 @@ class InteractiveImportRow extends Component {
|
||||||
language,
|
language,
|
||||||
size,
|
size,
|
||||||
rejections,
|
rejections,
|
||||||
|
isReprocessing,
|
||||||
isSelected,
|
isSelected,
|
||||||
onSelectedChange
|
onSelectedChange
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -189,7 +191,7 @@ class InteractiveImportRow extends Component {
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
const showSeriesPlaceholder = isSelected && !series;
|
const showSeriesPlaceholder = isSelected && !series;
|
||||||
const showSeasonNumberPlaceholder = isSelected && !!series && isNaN(seasonNumber);
|
const showSeasonNumberPlaceholder = isSelected && !!series && isNaN(seasonNumber) && !isReprocessing;
|
||||||
const showEpisodeNumbersPlaceholder = isSelected && Number.isInteger(seasonNumber) && !episodes.length;
|
const showEpisodeNumbersPlaceholder = isSelected && Number.isInteger(seasonNumber) && !episodes.length;
|
||||||
const showQualityPlaceholder = isSelected && !quality;
|
const showQualityPlaceholder = isSelected && !quality;
|
||||||
const showLanguagePlaceholder = isSelected && !language;
|
const showLanguagePlaceholder = isSelected && !language;
|
||||||
|
@ -227,6 +229,15 @@ class InteractiveImportRow extends Component {
|
||||||
{
|
{
|
||||||
showSeasonNumberPlaceholder ? <InteractiveImportRowCellPlaceholder /> : seasonNumber
|
showSeasonNumberPlaceholder ? <InteractiveImportRowCellPlaceholder /> : seasonNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isReprocessing && seasonNumber == null ?
|
||||||
|
<LoadingIndicator className={styles.reprocessing}
|
||||||
|
size={20}
|
||||||
|
|
||||||
|
/> : null
|
||||||
|
}
|
||||||
|
|
||||||
</TableRowCellButton>
|
</TableRowCellButton>
|
||||||
|
|
||||||
<TableRowCellButton
|
<TableRowCellButton
|
||||||
|
@ -363,6 +374,7 @@ InteractiveImportRow.propTypes = {
|
||||||
language: PropTypes.object,
|
language: 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
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import _ from 'lodash';
|
|
||||||
import PropTypes from 'prop-types';
|
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 createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
||||||
import SelectSeriesModalContent from './SelectSeriesModalContent';
|
import SelectSeriesModalContent from './SelectSeriesModalContent';
|
||||||
|
|
||||||
|
@ -29,7 +28,8 @@ function createMapStateToProps() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
updateInteractiveImportItem
|
dispatchReprocessInteractiveImportItems: reprocessInteractiveImportItems,
|
||||||
|
dispatchUpdateInteractiveImportItem: updateInteractiveImportItem
|
||||||
};
|
};
|
||||||
|
|
||||||
class SelectSeriesModalContentConnector extends Component {
|
class SelectSeriesModalContentConnector extends Component {
|
||||||
|
@ -38,10 +38,18 @@ class SelectSeriesModalContentConnector extends Component {
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onSeriesSelect = (seriesId) => {
|
onSeriesSelect = (seriesId) => {
|
||||||
const series = _.find(this.props.items, { id: seriesId });
|
const {
|
||||||
|
ids,
|
||||||
|
items,
|
||||||
|
dispatchUpdateInteractiveImportItem,
|
||||||
|
dispatchReprocessInteractiveImportItems,
|
||||||
|
onModalClose
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
this.props.ids.forEach((id) => {
|
const series = items.find((s) => s.id === seriesId);
|
||||||
this.props.updateInteractiveImportItem({
|
|
||||||
|
ids.forEach((id) => {
|
||||||
|
dispatchUpdateInteractiveImportItem({
|
||||||
id,
|
id,
|
||||||
series,
|
series,
|
||||||
seasonNumber: undefined,
|
seasonNumber: undefined,
|
||||||
|
@ -49,7 +57,9 @@ class SelectSeriesModalContentConnector extends Component {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.onModalClose(true);
|
dispatchReprocessInteractiveImportItems({ ids });
|
||||||
|
|
||||||
|
onModalClose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -68,7 +78,8 @@ class SelectSeriesModalContentConnector extends Component {
|
||||||
SelectSeriesModalContentConnector.propTypes = {
|
SelectSeriesModalContentConnector.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,
|
||||||
updateInteractiveImportItem: PropTypes.func.isRequired,
|
dispatchReprocessInteractiveImportItems: PropTypes.func.isRequired,
|
||||||
|
dispatchUpdateInteractiveImportItem: PropTypes.func.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { sortDirections } from 'Helpers/Props';
|
||||||
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
|
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
|
||||||
import createFetchHandler from './Creators/createFetchHandler';
|
import createFetchHandler from './Creators/createFetchHandler';
|
||||||
import createHandleActions from './Creators/createHandleActions';
|
import createHandleActions from './Creators/createHandleActions';
|
||||||
import { set, update } from './baseActions';
|
import { set, update, updateItem } from './baseActions';
|
||||||
|
|
||||||
//
|
//
|
||||||
// Variables
|
// Variables
|
||||||
|
@ -16,6 +16,8 @@ import { set, update } from './baseActions';
|
||||||
export const section = 'interactiveImport';
|
export const section = 'interactiveImport';
|
||||||
|
|
||||||
const episodesSection = `${section}.episodes`;
|
const episodesSection = `${section}.episodes`;
|
||||||
|
let abortCurrentRequest = null;
|
||||||
|
let currentIds = [];
|
||||||
|
|
||||||
//
|
//
|
||||||
// State
|
// State
|
||||||
|
@ -49,6 +51,7 @@ export const defaultState = {
|
||||||
|
|
||||||
episodes: {
|
episodes: {
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
|
isReprocessing: false,
|
||||||
isPopulated: false,
|
isPopulated: false,
|
||||||
error: null,
|
error: null,
|
||||||
sortKey: 'episodeNumber',
|
sortKey: 'episodeNumber',
|
||||||
|
@ -66,6 +69,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';
|
||||||
|
@ -82,6 +86,7 @@ export const CLEAR_INTERACTIVE_IMPORT_EPISODES = 'interactiveImport/clearInterac
|
||||||
// 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);
|
||||||
|
@ -133,6 +138,72 @@ export const actionHandlers = handleThunks({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[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,
|
||||||
|
seriesId: item.series.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
|
||||||
|
}))
|
||||||
|
));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
[FETCH_INTERACTIVE_IMPORT_EPISODES]: createFetchHandler('interactiveImport.episodes', '/episode')
|
[FETCH_INTERACTIVE_IMPORT_EPISODES]: createFetchHandler('interactiveImport.episodes', '/episode')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ 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.MediaFiles.EpisodeImport.Aggregation;
|
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation;
|
||||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Parser;
|
using NzbDrone.Core.Parser;
|
||||||
|
@ -22,6 +21,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||||
public interface IManualImportService
|
public interface IManualImportService
|
||||||
{
|
{
|
||||||
List<ManualImportItem> GetMediaFiles(string path, string downloadId, bool filterExistingFiles);
|
List<ManualImportItem> GetMediaFiles(string path, string downloadId, bool filterExistingFiles);
|
||||||
|
ManualImportItem ReprocessItem(string path, string downloadId, int seriesId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ManualImportService : IExecute<ManualImportCommand>, IManualImportService
|
public class ManualImportService : IExecute<ManualImportCommand>, IManualImportService
|
||||||
|
@ -32,7 +32,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||||
private readonly IMakeImportDecision _importDecisionMaker;
|
private readonly IMakeImportDecision _importDecisionMaker;
|
||||||
private readonly ISeriesService _seriesService;
|
private readonly ISeriesService _seriesService;
|
||||||
private readonly IEpisodeService _episodeService;
|
private readonly IEpisodeService _episodeService;
|
||||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
|
||||||
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
||||||
private readonly IAggregationService _aggregationService;
|
private readonly IAggregationService _aggregationService;
|
||||||
private readonly ITrackedDownloadService _trackedDownloadService;
|
private readonly ITrackedDownloadService _trackedDownloadService;
|
||||||
|
@ -46,7 +45,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||||
IMakeImportDecision importDecisionMaker,
|
IMakeImportDecision importDecisionMaker,
|
||||||
ISeriesService seriesService,
|
ISeriesService seriesService,
|
||||||
IEpisodeService episodeService,
|
IEpisodeService episodeService,
|
||||||
IVideoFileInfoReader videoFileInfoReader,
|
|
||||||
IAggregationService aggregationService,
|
IAggregationService aggregationService,
|
||||||
IImportApprovedEpisodes importApprovedEpisodes,
|
IImportApprovedEpisodes importApprovedEpisodes,
|
||||||
ITrackedDownloadService trackedDownloadService,
|
ITrackedDownloadService trackedDownloadService,
|
||||||
|
@ -60,7 +58,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||||
_importDecisionMaker = importDecisionMaker;
|
_importDecisionMaker = importDecisionMaker;
|
||||||
_seriesService = seriesService;
|
_seriesService = seriesService;
|
||||||
_episodeService = episodeService;
|
_episodeService = episodeService;
|
||||||
_videoFileInfoReader = videoFileInfoReader;
|
|
||||||
_aggregationService = aggregationService;
|
_aggregationService = aggregationService;
|
||||||
_importApprovedEpisodes = importApprovedEpisodes;
|
_importApprovedEpisodes = importApprovedEpisodes;
|
||||||
_trackedDownloadService = trackedDownloadService;
|
_trackedDownloadService = trackedDownloadService;
|
||||||
|
@ -97,6 +94,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||||
return ProcessFolder(path, path, downloadId, filterExistingFiles);
|
return ProcessFolder(path, path, downloadId, filterExistingFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ManualImportItem ReprocessItem(string path, string downloadId, int seriesId)
|
||||||
|
{
|
||||||
|
var rootFolder = Path.GetDirectoryName(path);
|
||||||
|
var series = _seriesService.GetSeries(seriesId);
|
||||||
|
|
||||||
|
return ProcessFile(rootFolder, rootFolder, path, downloadId, series);
|
||||||
|
}
|
||||||
|
|
||||||
private List<ManualImportItem> ProcessFolder(string rootFolder, string baseFolder, string downloadId, bool filterExistingFiles)
|
private List<ManualImportItem> ProcessFolder(string rootFolder, string baseFolder, string downloadId, bool filterExistingFiles)
|
||||||
{
|
{
|
||||||
DownloadClientItem downloadClientItem = null;
|
DownloadClientItem downloadClientItem = null;
|
||||||
|
@ -139,11 +144,15 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||||
return decisions.Select(decision => MapItem(decision, rootFolder, downloadId, directoryInfo.Name)).ToList();
|
return decisions.Select(decision => MapItem(decision, rootFolder, downloadId, directoryInfo.Name)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ManualImportItem ProcessFile(string rootFolder, string baseFolder, string file, string downloadId)
|
private ManualImportItem ProcessFile(string rootFolder, string baseFolder, string file, string downloadId, Series series = null)
|
||||||
{
|
{
|
||||||
DownloadClientItem downloadClientItem = null;
|
DownloadClientItem downloadClientItem = null;
|
||||||
var relativeFile = baseFolder.GetRelativePath(file);
|
var relativeFile = baseFolder.GetRelativePath(file);
|
||||||
var series = _parsingService.GetSeries(relativeFile.Split('\\', '/')[0]);
|
|
||||||
|
if (series == null)
|
||||||
|
{
|
||||||
|
_parsingService.GetSeries(relativeFile.Split('\\', '/')[0]);
|
||||||
|
}
|
||||||
|
|
||||||
if (series == null)
|
if (series == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Nancy;
|
||||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Manual;
|
using NzbDrone.Core.MediaFiles.EpisodeImport.Manual;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
|
using Sonarr.Api.V3.Episodes;
|
||||||
using Sonarr.Http;
|
using Sonarr.Http;
|
||||||
using Sonarr.Http.Extensions;
|
using Sonarr.Http.Extensions;
|
||||||
|
|
||||||
|
@ -17,6 +19,7 @@ namespace Sonarr.Api.V3.ManualImport
|
||||||
_manualImportService = manualImportService;
|
_manualImportService = manualImportService;
|
||||||
|
|
||||||
GetResourceAll = GetMediaFiles;
|
GetResourceAll = GetMediaFiles;
|
||||||
|
Post["/"] = x => ReprocessItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ManualImportResource> GetMediaFiles()
|
private List<ManualImportResource> GetMediaFiles()
|
||||||
|
@ -28,6 +31,21 @@ namespace Sonarr.Api.V3.ManualImport
|
||||||
return _manualImportService.GetMediaFiles(folder, downloadId, filterExistingFiles).ToResource().Select(AddQualityWeight).ToList();
|
return _manualImportService.GetMediaFiles(folder, downloadId, filterExistingFiles).ToResource().Select(AddQualityWeight).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Response ReprocessItems()
|
||||||
|
{
|
||||||
|
var items = Request.Body.FromJson<List<ManualImportReprocessResource>>();
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
var processedItem = _manualImportService.ReprocessItem(item.Path, item.DownloadId, item.SeriesId);
|
||||||
|
|
||||||
|
item.SeasonNumber = processedItem.SeasonNumber;
|
||||||
|
item.Episodes = processedItem.Episodes.ToResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.AsResponse();
|
||||||
|
}
|
||||||
|
|
||||||
private ManualImportResource AddQualityWeight(ManualImportResource item)
|
private ManualImportResource AddQualityWeight(ManualImportResource item)
|
||||||
{
|
{
|
||||||
if (item.Quality != null)
|
if (item.Quality != null)
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Sonarr.Api.V3.Episodes;
|
||||||
|
using Sonarr.Http.REST;
|
||||||
|
|
||||||
|
namespace Sonarr.Api.V3.ManualImport
|
||||||
|
{
|
||||||
|
public class ManualImportReprocessResource : RestResource
|
||||||
|
{
|
||||||
|
public string Path { get; set; }
|
||||||
|
public int SeriesId { get; set; }
|
||||||
|
public int? SeasonNumber { get; set; }
|
||||||
|
public List<EpisodeResource> Episodes { get; set; }
|
||||||
|
public string DownloadId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -93,6 +93,7 @@
|
||||||
<Compile Include="EpisodeFiles\MediaInfoResource.cs" />
|
<Compile Include="EpisodeFiles\MediaInfoResource.cs" />
|
||||||
<Compile Include="Indexers\ReleaseModuleBase.cs" />
|
<Compile Include="Indexers\ReleaseModuleBase.cs" />
|
||||||
<Compile Include="Indexers\ReleasePushModule.cs" />
|
<Compile Include="Indexers\ReleasePushModule.cs" />
|
||||||
|
<Compile Include="ManualImport\ManualImportReprocessResource.cs" />
|
||||||
<Compile Include="Parse\ParseModule.cs" />
|
<Compile Include="Parse\ParseModule.cs" />
|
||||||
<Compile Include="Parse\ParseResource.cs" />
|
<Compile Include="Parse\ParseResource.cs" />
|
||||||
<Compile Include="ManualImport\ManualImportModule.cs" />
|
<Compile Include="ManualImport\ManualImportModule.cs" />
|
||||||
|
|
Loading…
Reference in New Issue