parent
fb060730c7
commit
4c622fd412
|
@ -271,26 +271,32 @@ class EnhancedSelectInput extends Component {
|
||||||
this.setState({ isOpen: !this.state.isOpen });
|
this.setState({ isOpen: !this.state.isOpen });
|
||||||
};
|
};
|
||||||
|
|
||||||
onSelect = (value) => {
|
onSelect = (newValue) => {
|
||||||
if (Array.isArray(this.props.value)) {
|
const { name, value, values, onChange } = this.props;
|
||||||
let newValue = null;
|
const additionalProperties = values.find((v) => v.key === newValue)?.additionalProperties;
|
||||||
const index = this.props.value.indexOf(value);
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
let arrayValue = null;
|
||||||
|
const index = value.indexOf(newValue);
|
||||||
|
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
newValue = this.props.values.map((v) => v.key).filter((v) => (v === value) || this.props.value.includes(v));
|
arrayValue = values.map((v) => v.key).filter((v) => (v === newValue) || value.includes(v));
|
||||||
} else {
|
} else {
|
||||||
newValue = [...this.props.value];
|
arrayValue = [...value];
|
||||||
newValue.splice(index, 1);
|
arrayValue.splice(index, 1);
|
||||||
}
|
}
|
||||||
this.props.onChange({
|
onChange({
|
||||||
name: this.props.name,
|
name,
|
||||||
value: newValue
|
value: arrayValue,
|
||||||
|
additionalProperties
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({ isOpen: false });
|
this.setState({ isOpen: false });
|
||||||
|
|
||||||
this.props.onChange({
|
onChange({
|
||||||
name: this.props.name,
|
name,
|
||||||
value
|
value: newValue,
|
||||||
|
additionalProperties
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -485,7 +491,7 @@ class EnhancedSelectInput extends Component {
|
||||||
values.map((v, index) => {
|
values.map((v, index) => {
|
||||||
const hasParent = v.parentKey !== undefined;
|
const hasParent = v.parentKey !== undefined;
|
||||||
const depth = hasParent ? 1 : 0;
|
const depth = hasParent ? 1 : 0;
|
||||||
const parentSelected = hasParent && value.includes(v.parentKey);
|
const parentSelected = hasParent && Array.isArray(value) && value.includes(v.parentKey);
|
||||||
return (
|
return (
|
||||||
<OptionComponent
|
<OptionComponent
|
||||||
key={v.key}
|
key={v.key}
|
||||||
|
|
|
@ -9,7 +9,8 @@ import EnhancedSelectInput from './EnhancedSelectInput';
|
||||||
const importantFieldNames = [
|
const importantFieldNames = [
|
||||||
'baseUrl',
|
'baseUrl',
|
||||||
'apiPath',
|
'apiPath',
|
||||||
'apiKey'
|
'apiKey',
|
||||||
|
'authToken'
|
||||||
];
|
];
|
||||||
|
|
||||||
function getProviderDataKey(providerData) {
|
function getProviderDataKey(providerData) {
|
||||||
|
@ -34,7 +35,9 @@ function getSelectOptions(items) {
|
||||||
key: option.value,
|
key: option.value,
|
||||||
value: option.name,
|
value: option.name,
|
||||||
hint: option.hint,
|
hint: option.hint,
|
||||||
parentKey: option.parentValue
|
parentKey: option.parentValue,
|
||||||
|
isDisabled: option.isDisabled,
|
||||||
|
additionalProperties: option.additionalProperties
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -147,7 +150,7 @@ EnhancedSelectInputConnector.propTypes = {
|
||||||
provider: PropTypes.string.isRequired,
|
provider: PropTypes.string.isRequired,
|
||||||
providerData: PropTypes.object.isRequired,
|
providerData: PropTypes.object.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
value: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])).isRequired,
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.arrayOf(PropTypes.string), PropTypes.arrayOf(PropTypes.number)]).isRequired,
|
||||||
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
selectOptionsProviderAction: PropTypes.string,
|
selectOptionsProviderAction: PropTypes.string,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import {
|
import {
|
||||||
saveNotification,
|
saveNotification,
|
||||||
setNotificationFieldValue,
|
setNotificationFieldValues,
|
||||||
setNotificationValue,
|
setNotificationValue,
|
||||||
testNotification,
|
testNotification,
|
||||||
toggleAdvancedSettings
|
toggleAdvancedSettings
|
||||||
|
@ -27,7 +27,7 @@ function createMapStateToProps() {
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
setNotificationValue,
|
setNotificationValue,
|
||||||
setNotificationFieldValue,
|
setNotificationFieldValues,
|
||||||
saveNotification,
|
saveNotification,
|
||||||
testNotification,
|
testNotification,
|
||||||
toggleAdvancedSettings
|
toggleAdvancedSettings
|
||||||
|
@ -51,8 +51,8 @@ class EditNotificationModalContentConnector extends Component {
|
||||||
this.props.setNotificationValue({ name, value });
|
this.props.setNotificationValue({ name, value });
|
||||||
};
|
};
|
||||||
|
|
||||||
onFieldChange = ({ name, value }) => {
|
onFieldChange = ({ name, value, additionalProperties = {} }) => {
|
||||||
this.props.setNotificationFieldValue({ name, value });
|
this.props.setNotificationFieldValues({ properties: { ...additionalProperties, [name]: value } });
|
||||||
};
|
};
|
||||||
|
|
||||||
onSavePress = () => {
|
onSavePress = () => {
|
||||||
|
@ -91,7 +91,7 @@ EditNotificationModalContentConnector.propTypes = {
|
||||||
saveError: PropTypes.object,
|
saveError: PropTypes.object,
|
||||||
item: PropTypes.object.isRequired,
|
item: PropTypes.object.isRequired,
|
||||||
setNotificationValue: PropTypes.func.isRequired,
|
setNotificationValue: PropTypes.func.isRequired,
|
||||||
setNotificationFieldValue: PropTypes.func.isRequired,
|
setNotificationFieldValues: PropTypes.func.isRequired,
|
||||||
saveNotification: PropTypes.func.isRequired,
|
saveNotification: PropTypes.func.isRequired,
|
||||||
testNotification: PropTypes.func.isRequired,
|
testNotification: PropTypes.func.isRequired,
|
||||||
toggleAdvancedSettings: PropTypes.func.isRequired,
|
toggleAdvancedSettings: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import getSectionState from 'Utilities/State/getSectionState';
|
||||||
|
import updateSectionState from 'Utilities/State/updateSectionState';
|
||||||
|
|
||||||
|
function createSetProviderFieldValuesReducer(section) {
|
||||||
|
return (state, { payload }) => {
|
||||||
|
if (section === payload.section) {
|
||||||
|
const { properties } = payload;
|
||||||
|
const newState = getSectionState(state, section);
|
||||||
|
newState.pendingChanges = Object.assign({}, newState.pendingChanges);
|
||||||
|
const fields = Object.assign({}, newState.pendingChanges.fields || {});
|
||||||
|
|
||||||
|
Object.keys(properties).forEach((name) => {
|
||||||
|
fields[name] = properties[name];
|
||||||
|
});
|
||||||
|
|
||||||
|
newState.pendingChanges.fields = fields;
|
||||||
|
|
||||||
|
return updateSectionState(state, section, newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createSetProviderFieldValuesReducer;
|
|
@ -5,6 +5,7 @@ import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHand
|
||||||
import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler';
|
import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler';
|
||||||
import createTestProviderHandler, { createCancelTestProviderHandler } from 'Store/Actions/Creators/createTestProviderHandler';
|
import createTestProviderHandler, { createCancelTestProviderHandler } from 'Store/Actions/Creators/createTestProviderHandler';
|
||||||
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
|
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
|
||||||
|
import createSetProviderFieldValuesReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValuesReducer';
|
||||||
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
||||||
import { createThunk } from 'Store/thunks';
|
import { createThunk } from 'Store/thunks';
|
||||||
import selectProviderSchema from 'Utilities/State/selectProviderSchema';
|
import selectProviderSchema from 'Utilities/State/selectProviderSchema';
|
||||||
|
@ -22,6 +23,7 @@ export const FETCH_NOTIFICATION_SCHEMA = 'settings/notifications/fetchNotificati
|
||||||
export const SELECT_NOTIFICATION_SCHEMA = 'settings/notifications/selectNotificationSchema';
|
export const SELECT_NOTIFICATION_SCHEMA = 'settings/notifications/selectNotificationSchema';
|
||||||
export const SET_NOTIFICATION_VALUE = 'settings/notifications/setNotificationValue';
|
export const SET_NOTIFICATION_VALUE = 'settings/notifications/setNotificationValue';
|
||||||
export const SET_NOTIFICATION_FIELD_VALUE = 'settings/notifications/setNotificationFieldValue';
|
export const SET_NOTIFICATION_FIELD_VALUE = 'settings/notifications/setNotificationFieldValue';
|
||||||
|
export const SET_NOTIFICATION_FIELD_VALUES = 'settings/notifications/setNotificationFieldValues';
|
||||||
export const SAVE_NOTIFICATION = 'settings/notifications/saveNotification';
|
export const SAVE_NOTIFICATION = 'settings/notifications/saveNotification';
|
||||||
export const CANCEL_SAVE_NOTIFICATION = 'settings/notifications/cancelSaveNotification';
|
export const CANCEL_SAVE_NOTIFICATION = 'settings/notifications/cancelSaveNotification';
|
||||||
export const DELETE_NOTIFICATION = 'settings/notifications/deleteNotification';
|
export const DELETE_NOTIFICATION = 'settings/notifications/deleteNotification';
|
||||||
|
@ -55,6 +57,13 @@ export const setNotificationFieldValue = createAction(SET_NOTIFICATION_FIELD_VAL
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setNotificationFieldValues = createAction(SET_NOTIFICATION_FIELD_VALUES, (payload) => {
|
||||||
|
return {
|
||||||
|
section,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Details
|
// Details
|
||||||
|
|
||||||
|
@ -99,6 +108,7 @@ export default {
|
||||||
reducers: {
|
reducers: {
|
||||||
[SET_NOTIFICATION_VALUE]: createSetSettingValueReducer(section),
|
[SET_NOTIFICATION_VALUE]: createSetSettingValueReducer(section),
|
||||||
[SET_NOTIFICATION_FIELD_VALUE]: createSetProviderFieldValueReducer(section),
|
[SET_NOTIFICATION_FIELD_VALUE]: createSetProviderFieldValueReducer(section),
|
||||||
|
[SET_NOTIFICATION_FIELD_VALUES]: createSetProviderFieldValuesReducer(section),
|
||||||
|
|
||||||
[SELECT_NOTIFICATION_SCHEMA]: (state, { payload }) => {
|
[SELECT_NOTIFICATION_SCHEMA]: (state, { payload }) => {
|
||||||
return selectProviderSchema(state, section, payload, (selectedSchema) => {
|
return selectProviderSchema(state, section, payload, (selectedSchema) => {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Annotations
|
namespace NzbDrone.Core.Annotations
|
||||||
|
@ -59,13 +60,27 @@ namespace NzbDrone.Core.Annotations
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FieldSelectOption
|
public class FieldSelectOption<T>
|
||||||
|
where T : struct
|
||||||
{
|
{
|
||||||
public int Value { get; set; }
|
public T Value { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
public string Hint { get; set; }
|
public string Hint { get; set; }
|
||||||
public int? ParentValue { get; set; }
|
public T? ParentValue { get; set; }
|
||||||
|
public bool? IsDisabled { get; set; }
|
||||||
|
public Dictionary<string, object> AdditionalProperties { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FieldSelectStringOption
|
||||||
|
{
|
||||||
|
public string Value { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Order { get; set; }
|
||||||
|
public string Hint { get; set; }
|
||||||
|
public string ParentValue { get; set; }
|
||||||
|
public bool? IsDisabled { get; set; }
|
||||||
|
public Dictionary<string, object> AdditionalProperties { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum FieldType
|
public enum FieldType
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||||
{
|
{
|
||||||
public static class NewznabCategoryFieldOptionsConverter
|
public static class NewznabCategoryFieldOptionsConverter
|
||||||
{
|
{
|
||||||
public static List<FieldSelectOption> GetFieldSelectOptions(List<NewznabCategory> categories)
|
public static List<FieldSelectOption<int>> GetFieldSelectOptions(List<NewznabCategory> categories)
|
||||||
{
|
{
|
||||||
// Categories not relevant for Sonarr
|
// Categories not relevant for Sonarr
|
||||||
var ignoreCategories = new[] { 1000, 3000, 4000, 6000, 7000 };
|
var ignoreCategories = new[] { 1000, 3000, 4000, 6000, 7000 };
|
||||||
|
@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||||
// And maybe relevant for specific users
|
// And maybe relevant for specific users
|
||||||
var unimportantCategories = new[] { 0, 2000 };
|
var unimportantCategories = new[] { 0, 2000 };
|
||||||
|
|
||||||
var result = new List<FieldSelectOption>();
|
var result = new List<FieldSelectOption<int>>();
|
||||||
|
|
||||||
if (categories == null)
|
if (categories == null)
|
||||||
{
|
{
|
||||||
|
@ -41,7 +41,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||||
|
|
||||||
foreach (var category in categories.Where(cat => !ignoreCategories.Contains(cat.Id)).OrderBy(cat => unimportantCategories.Contains(cat.Id)).ThenBy(cat => cat.Id))
|
foreach (var category in categories.Where(cat => !ignoreCategories.Contains(cat.Id)).OrderBy(cat => unimportantCategories.Contains(cat.Id)).ThenBy(cat => cat.Id))
|
||||||
{
|
{
|
||||||
result.Add(new FieldSelectOption
|
result.Add(new FieldSelectOption<int>
|
||||||
{
|
{
|
||||||
Value = category.Id,
|
Value = category.Id,
|
||||||
Name = category.Name,
|
Name = category.Name,
|
||||||
|
@ -52,7 +52,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||||
{
|
{
|
||||||
foreach (var subcat in category.Subcategories.OrderBy(cat => cat.Id))
|
foreach (var subcat in category.Subcategories.OrderBy(cat => cat.Id))
|
||||||
{
|
{
|
||||||
result.Add(new FieldSelectOption
|
result.Add(new FieldSelectOption<int>
|
||||||
{
|
{
|
||||||
Value = subcat.Id,
|
Value = subcat.Id,
|
||||||
Name = subcat.Name,
|
Name = subcat.Name,
|
||||||
|
|
|
@ -1361,6 +1361,8 @@
|
||||||
"NotificationsNtfyValidationAuthorizationRequired": "Authorization is required",
|
"NotificationsNtfyValidationAuthorizationRequired": "Authorization is required",
|
||||||
"NotificationsPlexSettingsAuthToken": "Auth Token",
|
"NotificationsPlexSettingsAuthToken": "Auth Token",
|
||||||
"NotificationsPlexSettingsAuthenticateWithPlexTv": "Authenticate with Plex.tv",
|
"NotificationsPlexSettingsAuthenticateWithPlexTv": "Authenticate with Plex.tv",
|
||||||
|
"NotificationsPlexSettingsServer": "Server",
|
||||||
|
"NotificationsPlexSettingsServerHelpText": "Select server from plex.tv account after authenticating",
|
||||||
"NotificationsPlexValidationNoTvLibraryFound": "At least one TV library is required",
|
"NotificationsPlexValidationNoTvLibraryFound": "At least one TV library is required",
|
||||||
"NotificationsPushBulletSettingSenderId": "Sender ID",
|
"NotificationsPushBulletSettingSenderId": "Sender ID",
|
||||||
"NotificationsPushBulletSettingSenderIdHelpText": "The device ID to send notifications from, use device_iden in the device's URL on pushbullet.com (leave blank to send from yourself)",
|
"NotificationsPushBulletSettingSenderIdHelpText": "The device ID to send notifications from, use device_iden in the device's URL on pushbullet.com (leave blank to send from yourself)",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
@ -12,6 +13,7 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||||
{
|
{
|
||||||
string GetAuthToken(string clientIdentifier, int pinId);
|
string GetAuthToken(string clientIdentifier, int pinId);
|
||||||
bool Ping(string clientIdentifier, string authToken);
|
bool Ping(string clientIdentifier, string authToken);
|
||||||
|
List<PlexTvResource> GetResources(string clientIdentifier, string authToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PlexTvProxy : IPlexTvProxy
|
public class PlexTvProxy : IPlexTvProxy
|
||||||
|
@ -62,6 +64,33 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<PlexTvResource> GetResources(string clientIdentifier, string authToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Allows us to tell plex.tv that we're still active and tokens should not be expired.
|
||||||
|
|
||||||
|
var request = BuildRequest(clientIdentifier);
|
||||||
|
|
||||||
|
request.ResourceUrl = "/api/v2/resources";
|
||||||
|
request.AddQueryParam("includeHttps", 1);
|
||||||
|
request.AddQueryParam("clientID", clientIdentifier);
|
||||||
|
request.AddQueryParam("X-Plex-Token", authToken);
|
||||||
|
|
||||||
|
if (Json.TryDeserialize<List<PlexTvResource>>(ProcessRequest(request), out var response))
|
||||||
|
{
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// Catch all exceptions and log at trace, this information could be interesting in debugging, but expired tokens will be handled elsewhere.
|
||||||
|
_logger.Trace(e, "Unable to ping plex.tv");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<PlexTvResource>();
|
||||||
|
}
|
||||||
|
|
||||||
private HttpRequestBuilder BuildRequest(string clientIdentifier)
|
private HttpRequestBuilder BuildRequest(string clientIdentifier)
|
||||||
{
|
{
|
||||||
var requestBuilder = new HttpRequestBuilder("https://plex.tv")
|
var requestBuilder = new HttpRequestBuilder("https://plex.tv")
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||||
|
{
|
||||||
|
public class PlexTvResource
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public bool Owned { get; set; }
|
||||||
|
|
||||||
|
public List<PlexTvResourceConnection> Connections { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("provides")]
|
||||||
|
public string ProvidesRaw { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public List<string> Provides => ProvidesRaw.Split(",").ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlexTvResourceConnection
|
||||||
|
{
|
||||||
|
public string Uri { get; set; }
|
||||||
|
public string Protocol { get; set; }
|
||||||
|
public string Address { get; set; }
|
||||||
|
public int Port { get; set; }
|
||||||
|
public bool Local { get; set; }
|
||||||
|
public string Host => Uri.IsNullOrWhiteSpace() ? Address : new Uri(Uri).Host;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using NzbDrone.Common.Cache;
|
using NzbDrone.Common.Cache;
|
||||||
|
@ -14,6 +15,7 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||||
PlexTvSignInUrlResponse GetSignInUrl(string callbackUrl, int pinId, string pinCode);
|
PlexTvSignInUrlResponse GetSignInUrl(string callbackUrl, int pinId, string pinCode);
|
||||||
string GetAuthToken(int pinId);
|
string GetAuthToken(int pinId);
|
||||||
void Ping(string authToken);
|
void Ping(string authToken);
|
||||||
|
List<PlexTvResource> GetServers(string authToken);
|
||||||
HttpRequest GetWatchlist(string authToken, int pageSize, int pageOffset);
|
HttpRequest GetWatchlist(string authToken, int pageSize, int pageOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +95,16 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||||
_cache.Get(authToken, () => _proxy.Ping(_configService.PlexClientIdentifier, authToken), TimeSpan.FromHours(24));
|
_cache.Get(authToken, () => _proxy.Ping(_configService.PlexClientIdentifier, authToken), TimeSpan.FromHours(24));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<PlexTvResource> GetServers(string authToken)
|
||||||
|
{
|
||||||
|
Ping(authToken);
|
||||||
|
|
||||||
|
var clientIdentifier = _configService.PlexClientIdentifier;
|
||||||
|
var resources = _proxy.GetResources(clientIdentifier, authToken);
|
||||||
|
|
||||||
|
return resources.Where(r => r.Owned && r.Provides.Contains("server")).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public HttpRequest GetWatchlist(string authToken, int pageSize, int pageOffset)
|
public HttpRequest GetWatchlist(string authToken, int pageSize, int pageOffset)
|
||||||
{
|
{
|
||||||
Ping(authToken);
|
Ping(authToken);
|
||||||
|
|
|
@ -5,6 +5,7 @@ using FluentValidation.Results;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Cache;
|
using NzbDrone.Common.Cache;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.Exceptions;
|
using NzbDrone.Core.Exceptions;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Notifications.Plex.PlexTv;
|
using NzbDrone.Core.Notifications.Plex.PlexTv;
|
||||||
|
@ -193,6 +194,79 @@ namespace NzbDrone.Core.Notifications.Plex.Server
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action == "servers")
|
||||||
|
{
|
||||||
|
Settings.Validate().Filter("AuthToken").ThrowOnError();
|
||||||
|
|
||||||
|
if (Settings.AuthToken.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return new { };
|
||||||
|
}
|
||||||
|
|
||||||
|
var servers = _plexTvService.GetServers(Settings.AuthToken);
|
||||||
|
var options = servers.SelectMany(s =>
|
||||||
|
{
|
||||||
|
var result = new List<FieldSelectStringOption>();
|
||||||
|
|
||||||
|
// result.Add(new FieldSelectStringOption
|
||||||
|
// {
|
||||||
|
// Value = s.Name,
|
||||||
|
// Name = s.Name,
|
||||||
|
// IsDisabled = true
|
||||||
|
// });
|
||||||
|
|
||||||
|
s.Connections.ForEach(c =>
|
||||||
|
{
|
||||||
|
var isSecure = c.Protocol == "https";
|
||||||
|
var additionalProperties = new Dictionary<string, object>();
|
||||||
|
var hints = new List<string>();
|
||||||
|
|
||||||
|
additionalProperties.Add("host", c.Host);
|
||||||
|
additionalProperties.Add("port", c.Port);
|
||||||
|
additionalProperties.Add("useSsl", isSecure);
|
||||||
|
hints.Add(c.Local ? "Local" : "Remote");
|
||||||
|
|
||||||
|
if (isSecure)
|
||||||
|
{
|
||||||
|
hints.Add("Secure");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(new FieldSelectStringOption
|
||||||
|
{
|
||||||
|
Value = c.Uri,
|
||||||
|
Name = $"{s.Name} ({c.Host})",
|
||||||
|
Hint = string.Join(", ", hints),
|
||||||
|
AdditionalProperties = additionalProperties
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isSecure)
|
||||||
|
{
|
||||||
|
var uri = $"http://{c.Address}:{c.Port}";
|
||||||
|
var insecureAdditionalProperties = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
insecureAdditionalProperties.Add("host", c.Address);
|
||||||
|
insecureAdditionalProperties.Add("port", c.Port);
|
||||||
|
insecureAdditionalProperties.Add("useSsl", false);
|
||||||
|
|
||||||
|
result.Add(new FieldSelectStringOption
|
||||||
|
{
|
||||||
|
Value = uri,
|
||||||
|
Name = $"{s.Name} ({c.Address})",
|
||||||
|
Hint = c.Local ? "Local" : "Remote",
|
||||||
|
AdditionalProperties = insecureAdditionalProperties
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
options
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new { };
|
return new { };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
@ -22,40 +23,45 @@ namespace NzbDrone.Core.Notifications.Plex.Server
|
||||||
|
|
||||||
public PlexServerSettings()
|
public PlexServerSettings()
|
||||||
{
|
{
|
||||||
|
Host = "";
|
||||||
Port = 32400;
|
Port = 32400;
|
||||||
UpdateLibrary = true;
|
UpdateLibrary = true;
|
||||||
SignIn = "startOAuth";
|
SignIn = "startOAuth";
|
||||||
}
|
}
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "Host")]
|
[JsonIgnore]
|
||||||
|
[FieldDefinition(0, Label = "NotificationsPlexSettingsServer", Type = FieldType.Select, SelectOptionsProviderAction = "servers", HelpText = "NotificationsPlexSettingsServerHelpText")]
|
||||||
|
public string Server { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Host")]
|
||||||
public string Host { get; set; }
|
public string Host { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "Port")]
|
[FieldDefinition(2, Label = "Port")]
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "NotificationsSettingsUseSslHelpText")]
|
[FieldDefinition(3, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "NotificationsSettingsUseSslHelpText")]
|
||||||
[FieldToken(TokenField.HelpText, "UseSsl", "serviceName", "Plex")]
|
[FieldToken(TokenField.HelpText, "UseSsl", "serviceName", "Plex")]
|
||||||
public bool UseSsl { get; set; }
|
public bool UseSsl { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "ConnectionSettingsUrlBaseHelpText")]
|
[FieldDefinition(4, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "ConnectionSettingsUrlBaseHelpText")]
|
||||||
[FieldToken(TokenField.HelpText, "UrlBase", "connectionName", "Plex")]
|
[FieldToken(TokenField.HelpText, "UrlBase", "connectionName", "Plex")]
|
||||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/plex")]
|
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/plex")]
|
||||||
public string UrlBase { get; set; }
|
public string UrlBase { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(4, Label = "NotificationsPlexSettingsAuthToken", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey, Advanced = true)]
|
[FieldDefinition(5, Label = "NotificationsPlexSettingsAuthToken", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey, Advanced = true)]
|
||||||
public string AuthToken { get; set; }
|
public string AuthToken { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(5, Label = "NotificationsPlexSettingsAuthenticateWithPlexTv", Type = FieldType.OAuth)]
|
[FieldDefinition(6, Label = "NotificationsPlexSettingsAuthenticateWithPlexTv", Type = FieldType.OAuth)]
|
||||||
public string SignIn { get; set; }
|
public string SignIn { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(6, Label = "NotificationsSettingsUpdateLibrary", Type = FieldType.Checkbox)]
|
[FieldDefinition(7, Label = "NotificationsSettingsUpdateLibrary", Type = FieldType.Checkbox)]
|
||||||
public bool UpdateLibrary { get; set; }
|
public bool UpdateLibrary { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(7, Label = "NotificationsSettingsUpdateMapPathsFrom", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsFromHelpText")]
|
[FieldDefinition(8, Label = "NotificationsSettingsUpdateMapPathsFrom", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsFromHelpText")]
|
||||||
[FieldToken(TokenField.HelpText, "NotificationsSettingsUpdateMapPathsFrom", "serviceName", "Plex")]
|
[FieldToken(TokenField.HelpText, "NotificationsSettingsUpdateMapPathsFrom", "serviceName", "Plex")]
|
||||||
public string MapFrom { get; set; }
|
public string MapFrom { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(8, Label = "NotificationsSettingsUpdateMapPathsTo", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsToHelpText")]
|
[FieldDefinition(9, Label = "NotificationsSettingsUpdateMapPathsTo", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsToHelpText")]
|
||||||
[FieldToken(TokenField.HelpText, "NotificationsSettingsUpdateMapPathsTo", "serviceName", "Plex")]
|
[FieldToken(TokenField.HelpText, "NotificationsSettingsUpdateMapPathsTo", "serviceName", "Plex")]
|
||||||
public string MapTo { get; set; }
|
public string MapTo { get; set; }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue