mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-11 20:12:41 +02:00
New: Reprocess Language in Manual Import after Movie Selection
This commit is contained in:
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 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 @@ public class ManualImportService : IExecute<ManualImportCommand>, IManualImportS
|
|||||||
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 @@ public ManualImportService(IDiskProvider diskProvider,
|
|||||||
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 @@ public ManualImportService(IDiskProvider diskProvider,
|
|||||||
_trackedDownloadService = trackedDownloadService;
|
_trackedDownloadService = trackedDownloadService;
|
||||||
_downloadedMovieImportService = downloadedMovieImportService;
|
_downloadedMovieImportService = downloadedMovieImportService;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_config = config;
|
|
||||||
_historyService = historyService;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +90,14 @@ public List<ManualImportItem> GetMediaFiles(string path, string downloadId, int?
|
|||||||
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.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 @@ public ManualImportModule(IManualImportService manualImportService)
|
|||||||
_manualImportService = manualImportService;
|
_manualImportService = manualImportService;
|
||||||
|
|
||||||
GetResourceAll = GetMediaFiles;
|
GetResourceAll = GetMediaFiles;
|
||||||
|
Post("/", x => ReprocessItems());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ManualImportResource> GetMediaFiles()
|
private List<ManualImportResource> GetMediaFiles()
|
||||||
@ -29,6 +31,22 @@ private List<ManualImportResource> GetMediaFiles()
|
|||||||
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.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.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…
Reference in New Issue
Block a user