mirror of
https://github.com/Radarr/Radarr.git
synced 2024-10-03 22:57:18 +02:00
New: Reset Quality Definitions to default
(cherry picked from commit d5fff15f32fdb49768dcadd94c760678e650c884)
This commit is contained in:
parent
850bfdcf82
commit
ae8245c3c5
@ -22,7 +22,7 @@ import MediaManagementConnector from 'Settings/MediaManagement/MediaManagementCo
|
||||
import MetadataSettings from 'Settings/Metadata/MetadataSettings';
|
||||
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
|
||||
import Profiles from 'Settings/Profiles/Profiles';
|
||||
import Quality from 'Settings/Quality/Quality';
|
||||
import QualityConnector from 'Settings/Quality/QualityConnector';
|
||||
import Settings from 'Settings/Settings';
|
||||
import TagSettings from 'Settings/Tags/TagSettings';
|
||||
import UISettingsConnector from 'Settings/UI/UISettingsConnector';
|
||||
@ -143,7 +143,7 @@ function AppRoutes(props) {
|
||||
|
||||
<Route
|
||||
path="/settings/quality"
|
||||
component={Quality}
|
||||
component={QualityConnector}
|
||||
/>
|
||||
|
||||
<Route
|
||||
|
@ -15,6 +15,7 @@ export const REFRESH_MOVIE = 'RefreshMovie';
|
||||
export const RENAME_FILES = 'RenameFiles';
|
||||
export const RENAME_MOVIE = 'RenameMovie';
|
||||
export const RESET_API_KEY = 'ResetApiKey';
|
||||
export const RESET_QUALITY_DEFINITIONS = 'ResetQualityDefinitions';
|
||||
export const RSS_SYNC = 'RssSync';
|
||||
export const MOVIE_SEARCH = 'MoviesSearch';
|
||||
export const IMPORT_LIST_SYNC = 'ImportListSync';
|
||||
|
@ -9,6 +9,7 @@ import { fetchCommands, finishCommand, updateCommand } from 'Store/Actions/comma
|
||||
import { fetchMovies } from 'Store/Actions/movieActions';
|
||||
import { fetchQueue, fetchQueueDetails } from 'Store/Actions/queueActions';
|
||||
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||
import { fetchQualityDefinitions } from 'Store/Actions/settingsActions';
|
||||
import { fetchHealth } from 'Store/Actions/systemActions';
|
||||
import { fetchTagDetails, fetchTags } from 'Store/Actions/tagActions';
|
||||
import { repopulatePage } from 'Utilities/pagePopulator';
|
||||
@ -46,6 +47,7 @@ const mapDispatchToProps = {
|
||||
dispatchUpdateItem: updateItem,
|
||||
dispatchRemoveItem: removeItem,
|
||||
dispatchFetchHealth: fetchHealth,
|
||||
dispatchFetchQualityDefinitions: fetchQualityDefinitions,
|
||||
dispatchFetchQueue: fetchQueue,
|
||||
dispatchFetchQueueDetails: fetchQueueDetails,
|
||||
dispatchFetchRootFolders: fetchRootFolders,
|
||||
@ -216,6 +218,10 @@ class SignalRConnector extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
handleQualitydefinition = () => {
|
||||
this.props.dispatchFetchQualityDefinitions();
|
||||
};
|
||||
|
||||
handleQueue = () => {
|
||||
if (this.props.isQueuePopulated) {
|
||||
this.props.dispatchFetchQueue();
|
||||
@ -335,6 +341,7 @@ SignalRConnector.propTypes = {
|
||||
dispatchUpdateItem: PropTypes.func.isRequired,
|
||||
dispatchRemoveItem: PropTypes.func.isRequired,
|
||||
dispatchFetchHealth: PropTypes.func.isRequired,
|
||||
dispatchFetchQualityDefinitions: PropTypes.func.isRequired,
|
||||
dispatchFetchQueue: PropTypes.func.isRequired,
|
||||
dispatchFetchQueueDetails: PropTypes.func.isRequired,
|
||||
dispatchFetchRootFolders: PropTypes.func.isRequired,
|
||||
|
@ -1,9 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import QualityDefinitionsConnector from './Definition/QualityDefinitionsConnector';
|
||||
import ResetQualityDefinitionsModal from './Reset/ResetQualityDefinitionsModal';
|
||||
|
||||
class Quality extends Component {
|
||||
|
||||
@ -17,7 +22,8 @@ class Quality extends Component {
|
||||
|
||||
this.state = {
|
||||
isSaving: false,
|
||||
hasPendingChanges: false
|
||||
hasPendingChanges: false,
|
||||
isConfirmQualityDefinitionResetModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
@ -32,6 +38,14 @@ class Quality extends Component {
|
||||
this.setState(payload);
|
||||
};
|
||||
|
||||
onResetQualityDefinitionsPress = () => {
|
||||
this.setState({ isConfirmQualityDefinitionResetModalOpen: true });
|
||||
};
|
||||
|
||||
onCloseResetQualityDefinitionsModal = () => {
|
||||
this.setState({ isConfirmQualityDefinitionResetModalOpen: false });
|
||||
};
|
||||
|
||||
onSavePress = () => {
|
||||
if (this._saveCallback) {
|
||||
this._saveCallback();
|
||||
@ -44,6 +58,7 @@ class Quality extends Component {
|
||||
render() {
|
||||
const {
|
||||
isSaving,
|
||||
isResettingQualityDefinitions,
|
||||
hasPendingChanges
|
||||
} = this.state;
|
||||
|
||||
@ -52,6 +67,18 @@ class Quality extends Component {
|
||||
<SettingsToolbarConnector
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
additionalButtons={
|
||||
<Fragment>
|
||||
<PageToolbarSeparator />
|
||||
|
||||
<PageToolbarButton
|
||||
label={translate('ResetDefinitions')}
|
||||
iconName={icons.REFRESH}
|
||||
isSpinning={isResettingQualityDefinitions}
|
||||
onPress={this.onResetQualityDefinitionsPress}
|
||||
/>
|
||||
</Fragment>
|
||||
}
|
||||
onSavePress={this.onSavePress}
|
||||
/>
|
||||
|
||||
@ -61,9 +88,18 @@ class Quality extends Component {
|
||||
onChildStateChange={this.onChildStateChange}
|
||||
/>
|
||||
</PageContentBody>
|
||||
|
||||
<ResetQualityDefinitionsModal
|
||||
isOpen={this.state.isConfirmQualityDefinitionResetModalOpen}
|
||||
onModalClose={this.onCloseResetQualityDefinitionsModal}
|
||||
/>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Quality.propTypes = {
|
||||
isResettingQualityDefinitions: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
export default Quality;
|
||||
|
38
frontend/src/Settings/Quality/QualityConnector.js
Normal file
38
frontend/src/Settings/Quality/QualityConnector.js
Normal file
@ -0,0 +1,38 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import Quality from './Quality';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createCommandExecutingSelector(commandNames.RESET_QUALITY_DEFINITIONS),
|
||||
(isResettingQualityDefinitions) => {
|
||||
return {
|
||||
isResettingQualityDefinitions
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
class QualityConnector extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Quality
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QualityConnector.propTypes = {
|
||||
isResettingQualityDefinitions: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps)(QualityConnector);
|
@ -0,0 +1,33 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import ResetQualityDefinitionsModalContentConnector from './ResetQualityDefinitionsModalContentConnector';
|
||||
|
||||
function ResetQualityDefinitionsModal(props) {
|
||||
const {
|
||||
isOpen,
|
||||
onModalClose,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
size={sizes.MEDIUM}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<ResetQualityDefinitionsModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
ResetQualityDefinitionsModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ResetQualityDefinitionsModal;
|
@ -0,0 +1,3 @@
|
||||
.messageContainer {
|
||||
margin-bottom: 20px;
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import Button from 'Components/Link/Button';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ResetQualityDefinitionsModalContent.css';
|
||||
|
||||
class ResetQualityDefinitionsModalContent extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
resetDefinitionTitles: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onResetDefinitionTitlesChange = ({ value }) => {
|
||||
this.setState({ resetDefinitionTitles: value });
|
||||
};
|
||||
|
||||
onResetQualityDefinitionsConfirmed = () => {
|
||||
const resetDefinitionTitles = this.state.resetDefinitionTitles;
|
||||
|
||||
this.setState({ resetDefinitionTitles: false });
|
||||
this.props.onResetQualityDefinitions(resetDefinitionTitles);
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
onModalClose,
|
||||
isResettingQualityDefinitions
|
||||
} = this.props;
|
||||
|
||||
const resetDefinitionTitles = this.state.resetDefinitionTitles;
|
||||
|
||||
return (
|
||||
<ModalContent
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<ModalHeader>
|
||||
{translate('ResetQualityDefinitions')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div className={styles.messageContainer}>
|
||||
{translate('AreYouSureYouWantToResetQualityDefinitions')}
|
||||
</div>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('ResetTitles')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="resetDefinitionTitles"
|
||||
value={resetDefinitionTitles}
|
||||
helpText={translate('ResetTitlesHelpText')}
|
||||
onChange={this.onResetDefinitionTitlesChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={onModalClose}>
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
onPress={this.onResetQualityDefinitionsConfirmed}
|
||||
isDisabled={isResettingQualityDefinitions}
|
||||
>
|
||||
{translate('Reset')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ResetQualityDefinitionsModalContent.propTypes = {
|
||||
onResetQualityDefinitions: PropTypes.func.isRequired,
|
||||
isResettingQualityDefinitions: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ResetQualityDefinitionsModalContent;
|
@ -0,0 +1,54 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import ResetQualityDefinitionsModalContent from './ResetQualityDefinitionsModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createCommandExecutingSelector(commandNames.RESET_QUALITY_DEFINITIONS),
|
||||
(isResettingQualityDefinitions) => {
|
||||
return {
|
||||
isResettingQualityDefinitions
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
executeCommand
|
||||
};
|
||||
|
||||
class ResetQualityDefinitionsModalContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onResetQualityDefinitions = (resetTitles) => {
|
||||
this.props.executeCommand({ name: commandNames.RESET_QUALITY_DEFINITIONS, resetTitles });
|
||||
this.props.onModalClose(true);
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ResetQualityDefinitionsModalContent
|
||||
{...this.props}
|
||||
onResetQualityDefinitions={this.onResetQualityDefinitions}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ResetQualityDefinitionsModalContentConnector.propTypes = {
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
isResettingQualityDefinitions: PropTypes.bool.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(ResetQualityDefinitionsModalContentConnector);
|
@ -66,6 +66,7 @@
|
||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Are you sure you want to remove 1 item from the queue?",
|
||||
"AreYouSureYouWantToRemoveSelectedItemsFromQueue": "Are you sure you want to remove {0} items from the queue?",
|
||||
"AreYouSureYouWantToRemoveTheSelectedItemsFromBlocklist": "Are you sure you want to remove the selected items from the blocklist?",
|
||||
"AreYouSureYouWantToResetQualityDefinitions": "Are you sure you want to reset quality definitions?",
|
||||
"AreYouSureYouWantToResetYourAPIKey": "Are you sure you want to reset your API Key?",
|
||||
"AsAllDayHelpText": "Events will appear as all-day events in your calendar",
|
||||
"AudioInfo": "Audio Info",
|
||||
@ -846,6 +847,10 @@
|
||||
"RescanMovieFolderAfterRefresh": "Rescan Movie Folder after Refresh",
|
||||
"Reset": "Reset",
|
||||
"ResetAPIKey": "Reset API Key",
|
||||
"ResetDefinitions": "Reset Definitions",
|
||||
"ResetQualityDefinitions": "Reset Quality Definitions",
|
||||
"ResetTitles": "Reset Titles",
|
||||
"ResetTitlesHelpText": "Reset definition titles as well as values",
|
||||
"Restart": "Restart",
|
||||
"RestartNow": "Restart Now",
|
||||
"RestartRadarr": "Restart Radarr",
|
||||
|
@ -0,0 +1,14 @@
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
|
||||
namespace NzbDrone.Core.Qualities.Commands
|
||||
{
|
||||
public class ResetQualityDefinitionsCommand : Command
|
||||
{
|
||||
public bool ResetTitles { get; set; }
|
||||
|
||||
public ResetQualityDefinitionsCommand(bool resetTitles = false)
|
||||
{
|
||||
ResetTitles = resetTitles;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Qualities.Commands;
|
||||
|
||||
namespace NzbDrone.Core.Qualities
|
||||
{
|
||||
@ -17,7 +19,7 @@ public interface IQualityDefinitionService
|
||||
QualityDefinition Get(Quality quality);
|
||||
}
|
||||
|
||||
public class QualityDefinitionService : IQualityDefinitionService, IHandle<ApplicationStartedEvent>
|
||||
public class QualityDefinitionService : IQualityDefinitionService, IExecute<ResetQualityDefinitionsCommand>, IHandle<ApplicationStartedEvent>
|
||||
{
|
||||
private readonly IQualityDefinitionRepository _repo;
|
||||
private readonly ICached<Dictionary<Quality, QualityDefinition>> _cache;
|
||||
@ -105,5 +107,28 @@ public void Handle(ApplicationStartedEvent message)
|
||||
|
||||
InsertMissingDefinitions();
|
||||
}
|
||||
|
||||
public void Execute(ResetQualityDefinitionsCommand message)
|
||||
{
|
||||
List<QualityDefinition> updateList = new List<QualityDefinition>();
|
||||
|
||||
var allDefinitions = Quality.DefaultQualityDefinitions.OrderBy(d => d.Weight).ToList();
|
||||
var existingDefinitions = _repo.All().ToList();
|
||||
|
||||
foreach (var definition in allDefinitions)
|
||||
{
|
||||
var existing = existingDefinitions.SingleOrDefault(d => d.Quality == definition.Quality);
|
||||
|
||||
existing.MinSize = definition.MinSize;
|
||||
existing.MaxSize = definition.MaxSize;
|
||||
existing.Title = message.ResetTitles ? definition.Title : existing.Title;
|
||||
|
||||
updateList.Add(existing);
|
||||
}
|
||||
|
||||
_repo.UpdateMany(updateList);
|
||||
|
||||
_cache.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.SignalR;
|
||||
using Radarr.Http;
|
||||
using Radarr.Http.REST;
|
||||
using Radarr.Http.REST.Attributes;
|
||||
@ -9,11 +12,12 @@
|
||||
namespace Radarr.Api.V3.Qualities
|
||||
{
|
||||
[V3ApiController]
|
||||
public class QualityDefinitionController : RestController<QualityDefinitionResource>
|
||||
public class QualityDefinitionController : RestControllerWithSignalR<QualityDefinitionResource, QualityDefinition>, IHandle<CommandExecutedEvent>
|
||||
{
|
||||
private readonly IQualityDefinitionService _qualityDefinitionService;
|
||||
|
||||
public QualityDefinitionController(IQualityDefinitionService qualityDefinitionService)
|
||||
public QualityDefinitionController(IQualityDefinitionService qualityDefinitionService, IBroadcastSignalRMessage signalRBroadcaster)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_qualityDefinitionService = qualityDefinitionService;
|
||||
}
|
||||
@ -40,7 +44,7 @@ public List<QualityDefinitionResource> GetAll()
|
||||
[HttpPut("update")]
|
||||
public object UpdateMany([FromBody] List<QualityDefinitionResource> resource)
|
||||
{
|
||||
//Read from request
|
||||
// Read from request
|
||||
var qualityDefinitions = resource
|
||||
.ToModel()
|
||||
.ToList();
|
||||
@ -50,5 +54,14 @@ public object UpdateMany([FromBody] List<QualityDefinitionResource> resource)
|
||||
return Accepted(_qualityDefinitionService.All()
|
||||
.ToResource());
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public void Handle(CommandExecutedEvent message)
|
||||
{
|
||||
if (message.Command.Name == "ResetQualityDefinitions")
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Sync);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user