New: Allow Radarr List Sync by Source Tag

Fixes #4826
This commit is contained in:
Qstick 2020-08-18 23:21:44 -04:00
parent 5a1b82e195
commit 56184905a9
10 changed files with 79 additions and 19 deletions

View File

@ -8,16 +8,28 @@ import DeviceInput from './DeviceInput';
function createMapStateToProps() {
return createSelector(
(state, { value }) => value,
(state, { name }) => name,
(state) => state.providerOptions,
(value, devices) => {
(value, name, devices) => {
const {
isFetching,
isPopulated,
error,
items
} = devices;
return {
...devices,
isFetching,
isPopulated,
error,
items: items[name] || [],
selectedDevices: value.map((valueDevice) => {
const sectionItems = items[name] || [];
// Disable equality ESLint rule so we don't need to worry about
// a type mismatch between the value items and the device ID.
// eslint-disable-next-line eqeqeq
const device = devices.items.find((d) => d.id == valueDevice);
const device = sectionItems.find((d) => d.id == valueDevice);
if (device) {
return {
@ -61,11 +73,14 @@ class DeviceInputConnector extends Component {
const {
provider,
providerData,
dispatchFetchOptions
dispatchFetchOptions,
requestAction,
name
} = this.props;
dispatchFetchOptions({
action: 'getDevices',
action: requestAction,
itemSection: name,
provider,
providerData
});
@ -94,6 +109,7 @@ class DeviceInputConnector extends Component {
DeviceInputConnector.propTypes = {
provider: PropTypes.string.isRequired,
providerData: PropTypes.object.isRequired,
requestAction: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
dispatchFetchOptions: PropTypes.func.isRequired,

View File

@ -62,6 +62,7 @@ function ProviderFieldFormGroup(props) {
value,
type,
advanced,
requestAction,
hidden,
pending,
errors,
@ -98,6 +99,7 @@ function ProviderFieldFormGroup(props) {
pending={pending}
includeFiles={type === 'filePath' ? true : undefined}
onChange={onChange}
requestAction={requestAction}
{...otherProps}
/>
</FormGroup>
@ -118,6 +120,7 @@ ProviderFieldFormGroup.propTypes = {
value: PropTypes.any,
type: PropTypes.string.isRequired,
advanced: PropTypes.bool.isRequired,
requestAction: PropTypes.string,
hidden: PropTypes.string,
pending: PropTypes.bool.isRequired,
errors: PropTypes.arrayOf(PropTypes.object).isRequired,

View File

@ -14,7 +14,7 @@ export const section = 'providerOptions';
// State
export const defaultState = {
items: [],
items: {},
isFetching: false,
isPopulated: false,
error: false
@ -43,15 +43,20 @@ export const actionHandlers = handleThunks({
isFetching: true
}));
const oldItems = getState().providerOptions.items;
const itemSection = payload.itemSection;
const promise = requestAction(payload);
promise.done((data) => {
oldItems[itemSection] = data.options || [];
dispatch(set({
section,
isFetching: false,
isPopulated: true,
error: null,
items: data.options || []
items: oldItems
}));
});

View File

@ -20,6 +20,7 @@ namespace NzbDrone.Core.Annotations
public Type SelectOptions { get; set; }
public string Section { get; set; }
public HiddenType Hidden { get; set; }
public string RequestAction { get; set; }
}
public enum FieldType

View File

@ -16,6 +16,7 @@ namespace NzbDrone.Core.NetImport.Radarr
public int Year { get; set; }
public string TitleSlug { get; set; }
public int QualityProfileId { get; set; }
public HashSet<int> Tags { get; set; }
}
public class RadarrProfile
@ -23,4 +24,10 @@ namespace NzbDrone.Core.NetImport.Radarr
public string Name { get; set; }
public int Id { get; set; }
}
public class RadarrTag
{
public string Label { get; set; }
public int Id { get; set; }
}
}

View File

@ -41,7 +41,8 @@ namespace NzbDrone.Core.NetImport.Radarr
foreach (var remoteMovie in remoteMovies)
{
if (!Settings.ProfileIds.Any() || Settings.ProfileIds.Contains(remoteMovie.QualityProfileId))
if ((!Settings.ProfileIds.Any() || Settings.ProfileIds.Contains(remoteMovie.QualityProfileId)) &&
(!Settings.TagIds.Any() || Settings.TagIds.Any(x => remoteMovie.Tags.Any(y => y == x))))
{
movies.Add(new Movie
{
@ -76,19 +77,19 @@ namespace NzbDrone.Core.NetImport.Radarr
public override object RequestAction(string action, IDictionary<string, string> query)
{
if (action == "getDevices")
// Return early if there is not an API key
if (Settings.ApiKey.IsNullOrWhiteSpace())
{
// Return early if there is not an API key
if (Settings.ApiKey.IsNullOrWhiteSpace())
return new
{
return new
{
devices = new List<object>()
};
}
devices = new List<object>()
};
}
Settings.Validate().Filter("ApiKey").ThrowOnError();
Settings.Validate().Filter("ApiKey").ThrowOnError();
if (action == "getProfiles")
{
var devices = _radarrV3Proxy.GetProfiles(Settings);
return new
@ -102,6 +103,21 @@ namespace NzbDrone.Core.NetImport.Radarr
};
}
if (action == "getTags")
{
var devices = _radarrV3Proxy.GetTags(Settings);
return new
{
options = devices.OrderBy(d => d.Label, StringComparer.InvariantCultureIgnoreCase)
.Select(d => new
{
id = d.Id,
name = d.Label
})
};
}
return new { };
}

View File

@ -24,6 +24,7 @@ namespace NzbDrone.Core.NetImport.Radarr
BaseUrl = "";
ApiKey = "";
ProfileIds = new int[] { };
TagIds = new int[] { };
}
[FieldDefinition(0, Label = "Full URL", HelpText = "URL, including port, of the Radarr V3 instance to import from")]
@ -32,9 +33,12 @@ namespace NzbDrone.Core.NetImport.Radarr
[FieldDefinition(1, Label = "API Key", HelpText = "Apikey of the Radarr V3 instance to import from")]
public string ApiKey { get; set; }
[FieldDefinition(2, Type = FieldType.Device, Label = "Profiles", HelpText = "Profiles from the source instance to import from")]
[FieldDefinition(2, Type = FieldType.Device, RequestAction = "getProfiles", Label = "Profiles", HelpText = "Profiles from the source instance to import from")]
public IEnumerable<int> ProfileIds { get; set; }
[FieldDefinition(3, Type = FieldType.Device, RequestAction = "getTags", Label = "Tags", HelpText = "Tags from the source instance to import from")]
public IEnumerable<int> TagIds { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@ -13,6 +13,7 @@ namespace NzbDrone.Core.NetImport.Radarr
{
List<RadarrMovie> GetMovies(RadarrSettings settings);
List<RadarrProfile> GetProfiles(RadarrSettings settings);
List<RadarrTag> GetTags(RadarrSettings settings);
ValidationFailure Test(RadarrSettings settings);
}
@ -37,6 +38,11 @@ namespace NzbDrone.Core.NetImport.Radarr
return Execute<RadarrProfile>("/api/v3/qualityprofile", settings);
}
public List<RadarrTag> GetTags(RadarrSettings settings)
{
return Execute<RadarrTag>("/api/v3/tag", settings);
}
public ValidationFailure Test(RadarrSettings settings)
{
try

View File

@ -17,6 +17,7 @@ namespace Radarr.Http.ClientSchema
public List<SelectOption> SelectOptions { get; set; }
public string Section { get; set; }
public string Hidden { get; set; }
public string RequestAction { get; set; }
public Field Clone()
{

View File

@ -100,7 +100,8 @@ namespace Radarr.Http.ClientSchema
Order = fieldAttribute.Order,
Advanced = fieldAttribute.Advanced,
Type = fieldAttribute.Type.ToString().FirstCharToLower(),
Section = fieldAttribute.Section
Section = fieldAttribute.Section,
RequestAction = fieldAttribute.RequestAction
};
if (fieldAttribute.Type == FieldType.Select || fieldAttribute.Type == FieldType.TagSelect)