mirror of
https://github.com/Sonarr/Sonarr.git
synced 2025-04-23 13:57:06 -04:00
Convert Media Management settings to TypeScript
This commit is contained in:
parent
839658a698
commit
27f81117ed
10 changed files with 605 additions and 779 deletions
|
@ -15,7 +15,7 @@ import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadCl
|
|||
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
|
||||
import ImportListSettingsConnector from 'Settings/ImportLists/ImportListSettingsConnector';
|
||||
import IndexerSettingsConnector from 'Settings/Indexers/IndexerSettingsConnector';
|
||||
import MediaManagementConnector from 'Settings/MediaManagement/MediaManagementConnector';
|
||||
import MediaManagement from 'Settings/MediaManagement/MediaManagement';
|
||||
import MetadataSettings from 'Settings/Metadata/MetadataSettings';
|
||||
import MetadataSourceSettings from 'Settings/MetadataSource/MetadataSourceSettings';
|
||||
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
|
||||
|
@ -98,10 +98,7 @@ function AppRoutes() {
|
|||
|
||||
<Route exact={true} path="/settings" component={Settings} />
|
||||
|
||||
<Route
|
||||
path="/settings/mediamanagement"
|
||||
component={MediaManagementConnector}
|
||||
/>
|
||||
<Route path="/settings/mediamanagement" component={MediaManagement} />
|
||||
|
||||
<Route path="/settings/profiles" component={Profiles} />
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import Notification from 'typings/Notification';
|
|||
import QualityDefinition from 'typings/QualityDefinition';
|
||||
import QualityProfile from 'typings/QualityProfile';
|
||||
import General from 'typings/Settings/General';
|
||||
import MediaManagement from 'typings/Settings/MediaManagement';
|
||||
import NamingConfig from 'typings/Settings/NamingConfig';
|
||||
import NamingExample from 'typings/Settings/NamingExample';
|
||||
import ReleaseProfile from 'typings/Settings/ReleaseProfile';
|
||||
|
@ -54,6 +55,10 @@ export interface GeneralAppState
|
|||
extends AppSectionItemState<General>,
|
||||
AppSectionSaveState {}
|
||||
|
||||
export interface MediaManagementAppState
|
||||
extends AppSectionItemState<MediaManagement>,
|
||||
AppSectionSaveState {}
|
||||
|
||||
export interface NamingAppState
|
||||
extends AppSectionItemState<NamingConfig>,
|
||||
AppSectionSaveState {}
|
||||
|
@ -131,6 +136,7 @@ interface SettingsAppState {
|
|||
indexerFlags: IndexerFlagSettingsAppState;
|
||||
indexers: IndexerAppState;
|
||||
languages: LanguageSettingsAppState;
|
||||
mediaManagement: MediaManagementAppState;
|
||||
metadata: MetadataAppState;
|
||||
naming: NamingAppState;
|
||||
namingExamples: NamingExamplesAppState;
|
||||
|
|
8
frontend/src/Helpers/Hooks/useIsWindows.ts
Normal file
8
frontend/src/Helpers/Hooks/useIsWindows.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { useSelector } from 'react-redux';
|
||||
import AppState from 'App/State/AppState';
|
||||
|
||||
function useIsWindows() {
|
||||
return useSelector((state: AppState) => state.system.status.item.isWindows);
|
||||
}
|
||||
|
||||
export default useIsWindows;
|
|
@ -1,149 +0,0 @@
|
|||
// https://github.com/react-bootstrap/react-element-children
|
||||
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Iterates through children that are typically specified as `props.children`,
|
||||
* but only maps over children that are "valid components".
|
||||
*
|
||||
* The mapFunction provided index will be normalised to the components mapped,
|
||||
* so an invalid component would not increase the index.
|
||||
*
|
||||
* @param {?*} children Children tree container.
|
||||
* @param {function(*, int)} func.
|
||||
* @param {*} context Context for func.
|
||||
* @return {object} Object containing the ordered map of results.
|
||||
*/
|
||||
export function map(children, func, context) {
|
||||
let index = 0;
|
||||
|
||||
return React.Children.map(children, (child) => {
|
||||
if (!React.isValidElement(child)) {
|
||||
return child;
|
||||
}
|
||||
|
||||
return func.call(context, child, index++);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through children that are "valid components".
|
||||
*
|
||||
* The provided forEachFunc(child, index) will be called for each
|
||||
* leaf child with the index reflecting the position relative to "valid components".
|
||||
*
|
||||
* @param {?*} children Children tree container.
|
||||
* @param {function(*, int)} func.
|
||||
* @param {*} context Context for context.
|
||||
*/
|
||||
export function forEach(children, func, context) {
|
||||
let index = 0;
|
||||
|
||||
React.Children.forEach(children, (child) => {
|
||||
if (!React.isValidElement(child)) {
|
||||
return;
|
||||
}
|
||||
|
||||
func.call(context, child, index++);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of "valid components" in the Children container.
|
||||
*
|
||||
* @param {?*} children Children tree container.
|
||||
* @returns {number}
|
||||
*/
|
||||
export function count(children) {
|
||||
let result = 0;
|
||||
|
||||
React.Children.forEach(children, (child) => {
|
||||
if (!React.isValidElement(child)) {
|
||||
return;
|
||||
}
|
||||
|
||||
++result;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds children that are typically specified as `props.children`,
|
||||
* but only iterates over children that are "valid components".
|
||||
*
|
||||
* The provided forEachFunc(child, index) will be called for each
|
||||
* leaf child with the index reflecting the position relative to "valid components".
|
||||
*
|
||||
* @param {?*} children Children tree container.
|
||||
* @param {function(*, int)} func.
|
||||
* @param {*} context Context for func.
|
||||
* @returns {array} of children that meet the func return statement
|
||||
*/
|
||||
export function filter(children, func, context) {
|
||||
const result = [];
|
||||
|
||||
forEach(children, (child, index) => {
|
||||
if (func.call(context, child, index)) {
|
||||
result.push(child);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function find(children, func, context) {
|
||||
let result = null;
|
||||
|
||||
forEach(children, (child, index) => {
|
||||
if (result) {
|
||||
return;
|
||||
}
|
||||
if (func.call(context, child, index)) {
|
||||
result = child;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function every(children, func, context) {
|
||||
let result = true;
|
||||
|
||||
forEach(children, (child, index) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
if (!func.call(context, child, index)) {
|
||||
result = false;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function some(children, func, context) {
|
||||
let result = false;
|
||||
|
||||
forEach(children, (child, index) => {
|
||||
if (result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (func.call(context, child, index)) {
|
||||
result = true;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toArray(children) {
|
||||
const result = [];
|
||||
|
||||
forEach(children, (child) => {
|
||||
result.push(child);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export default function getDisplayName(Component) {
|
||||
return Component.displayName || Component.name || 'Component';
|
||||
}
|
|
@ -1,536 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import Form from 'Components/Form/Form';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import RootFolders from 'RootFolder/RootFolders';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import Naming from './Naming/Naming';
|
||||
import AddRootFolder from './RootFolder/AddRootFolder';
|
||||
|
||||
const episodeTitleRequiredOptions = [
|
||||
{
|
||||
key: 'always',
|
||||
get value() {
|
||||
return translate('Always');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'bulkSeasonReleases',
|
||||
get value() {
|
||||
return translate('OnlyForBulkSeasonReleases');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'never',
|
||||
get value() {
|
||||
return translate('Never');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const rescanAfterRefreshOptions = [
|
||||
{
|
||||
key: 'always',
|
||||
get value() {
|
||||
return translate('Always');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'afterManual',
|
||||
get value() {
|
||||
return translate('AfterManualRefresh');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'never',
|
||||
get value() {
|
||||
return translate('Never');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const downloadPropersAndRepacksOptions = [
|
||||
{
|
||||
key: 'preferAndUpgrade',
|
||||
get value() {
|
||||
return translate('PreferAndUpgrade');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'doNotUpgrade',
|
||||
get value() {
|
||||
return translate('DoNotUpgradeAutomatically');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'doNotPrefer',
|
||||
get value() {
|
||||
return translate('DoNotPrefer');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const fileDateOptions = [
|
||||
{
|
||||
key: 'none',
|
||||
get value() {
|
||||
return translate('None');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'localAirDate',
|
||||
get value() {
|
||||
return translate('LocalAirDate');
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'utcAirDate',
|
||||
get value() {
|
||||
return translate('UtcAirDate');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
class MediaManagement extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
advancedSettings,
|
||||
isFetching,
|
||||
error,
|
||||
settings,
|
||||
hasSettings,
|
||||
isWindows,
|
||||
onInputChange,
|
||||
onSavePress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<PageContent title={translate('MediaManagementSettings')}>
|
||||
<SettingsToolbar
|
||||
advancedSettings={advancedSettings}
|
||||
{...otherProps}
|
||||
onSavePress={onSavePress}
|
||||
/>
|
||||
|
||||
<PageContentBody>
|
||||
<Naming />
|
||||
|
||||
{
|
||||
isFetching ?
|
||||
<FieldSet legend={translate('NamingSettings')}>
|
||||
<LoadingIndicator />
|
||||
</FieldSet> : null
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && error ?
|
||||
<FieldSet legend={translate('NamingSettings')}>
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('MediaManagementSettingsLoadError')}
|
||||
</Alert>
|
||||
</FieldSet> : null
|
||||
}
|
||||
|
||||
{
|
||||
hasSettings && !isFetching && !error ?
|
||||
<Form
|
||||
id="mediaManagementSettings"
|
||||
{...otherProps}
|
||||
>
|
||||
{
|
||||
advancedSettings ?
|
||||
<FieldSet legend={translate('Folders')}>
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('CreateEmptySeriesFolders')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="createEmptySeriesFolders"
|
||||
helpText={translate('CreateEmptySeriesFoldersHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.createEmptySeriesFolders}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('DeleteEmptyFolders')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="deleteEmptyFolders"
|
||||
helpText={translate('DeleteEmptySeriesFoldersHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.deleteEmptyFolders}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet> : null
|
||||
}
|
||||
|
||||
{
|
||||
advancedSettings ?
|
||||
<FieldSet
|
||||
legend={translate('Importing')}
|
||||
>
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
<FormLabel>{translate('EpisodeTitleRequired')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="episodeTitleRequired"
|
||||
helpText={translate('EpisodeTitleRequiredHelpText')}
|
||||
values={episodeTitleRequiredOptions}
|
||||
onChange={onInputChange}
|
||||
{...settings.episodeTitleRequired}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('SkipFreeSpaceCheck')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="skipFreeSpaceCheckWhenImporting"
|
||||
helpText={translate('SkipFreeSpaceCheckHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.skipFreeSpaceCheckWhenImporting}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('MinimumFreeSpace')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
unit='MB'
|
||||
name="minimumFreeSpaceWhenImporting"
|
||||
helpText={translate('MinimumFreeSpaceHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.minimumFreeSpaceWhenImporting}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('UseHardlinksInsteadOfCopy')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="copyUsingHardlinks"
|
||||
helpText={translate('CopyUsingHardlinksSeriesHelpText')}
|
||||
helpTextWarning={translate('CopyUsingHardlinksHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...settings.copyUsingHardlinks}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('ImportUsingScript')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="useScriptImport"
|
||||
helpText={translate('ImportUsingScriptHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.useScriptImport}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{
|
||||
settings.useScriptImport.value ?
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ImportScriptPath')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.PATH}
|
||||
includeFiles={true}
|
||||
name="scriptImportPath"
|
||||
helpText={translate('ImportScriptPathHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.scriptImportPath}
|
||||
/>
|
||||
</FormGroup> : null
|
||||
}
|
||||
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>{translate('ImportExtraFiles')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="importExtraFiles"
|
||||
helpText={translate('ImportExtraFilesEpisodeHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.importExtraFiles}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{
|
||||
settings.importExtraFiles.value ?
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ImportExtraFiles')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="extraFileExtensions"
|
||||
helpTexts={[
|
||||
translate('ExtraFileExtensionsHelpText'),
|
||||
translate('ExtraFileExtensionsHelpTextsExamples')
|
||||
]}
|
||||
onChange={onInputChange}
|
||||
{...settings.extraFileExtensions}
|
||||
/>
|
||||
</FormGroup> : null
|
||||
}
|
||||
</FieldSet> : null
|
||||
}
|
||||
|
||||
<FieldSet
|
||||
legend={translate('FileManagement')}
|
||||
>
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>{translate('UnmonitorDeletedEpisodes')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="autoUnmonitorPreviouslyDownloadedEpisodes"
|
||||
helpText={translate('UnmonitorDeletedEpisodesHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.autoUnmonitorPreviouslyDownloadedEpisodes}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('DownloadPropersAndRepacks')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="downloadPropersAndRepacks"
|
||||
helpTexts={[
|
||||
translate('DownloadPropersAndRepacksHelpText'),
|
||||
translate('DownloadPropersAndRepacksHelpTextCustomFormat')
|
||||
]}
|
||||
helpTextWarning={
|
||||
settings.downloadPropersAndRepacks.value === 'doNotPrefer' ?
|
||||
translate('DownloadPropersAndRepacksHelpTextWarning') :
|
||||
undefined
|
||||
}
|
||||
values={downloadPropersAndRepacksOptions}
|
||||
onChange={onInputChange}
|
||||
{...settings.downloadPropersAndRepacks}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('AnalyseVideoFiles')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableMediaInfo"
|
||||
helpText={translate('AnalyseVideoFilesHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.enableMediaInfo}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('RescanSeriesFolderAfterRefresh')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="rescanAfterRefresh"
|
||||
helpText={translate('RescanAfterRefreshSeriesHelpText')}
|
||||
helpTextWarning={translate('RescanAfterRefreshHelpTextWarning')}
|
||||
values={rescanAfterRefreshOptions}
|
||||
onChange={onInputChange}
|
||||
{...settings.rescanAfterRefresh}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ChangeFileDate')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="fileDate"
|
||||
helpText={translate('ChangeFileDateHelpText')}
|
||||
values={fileDateOptions}
|
||||
onChange={onInputChange}
|
||||
{...settings.fileDate}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('RecyclingBin')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.PATH}
|
||||
name="recycleBin"
|
||||
helpText={translate('RecyclingBinHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.recycleBin}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('RecyclingBinCleanup')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="recycleBinCleanupDays"
|
||||
helpText={translate('RecyclingBinCleanupHelpText')}
|
||||
helpTextWarning={translate('RecyclingBinCleanupHelpTextWarning')}
|
||||
min={0}
|
||||
onChange={onInputChange}
|
||||
{...settings.recycleBinCleanupDays}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet>
|
||||
|
||||
{
|
||||
advancedSettings && !isWindows ?
|
||||
<FieldSet
|
||||
legend={translate('Permissions')}
|
||||
>
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('SetPermissions')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="setPermissionsLinux"
|
||||
helpText={translate('SetPermissionsLinuxHelpText')}
|
||||
helpTextWarning={translate('SetPermissionsLinuxHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...settings.setPermissionsLinux}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ChmodFolder')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.UMASK}
|
||||
name="chmodFolder"
|
||||
helpText={translate('ChmodFolderHelpText')}
|
||||
helpTextWarning={translate('ChmodFolderHelpTextWarning')}
|
||||
onChange={onInputChange}
|
||||
{...settings.chmodFolder}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ChownGroup')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="chownGroup"
|
||||
helpText={translate('ChownGroupHelpText')}
|
||||
helpTextWarning={translate('ChownGroupHelpTextWarning')}
|
||||
values={fileDateOptions}
|
||||
onChange={onInputChange}
|
||||
{...settings.chownGroup}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet> : null
|
||||
}
|
||||
</Form> : null
|
||||
}
|
||||
|
||||
<FieldSet legend={translate('RootFolders')}>
|
||||
<RootFolders />
|
||||
<AddRootFolder />
|
||||
</FieldSet>
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MediaManagement.propTypes = {
|
||||
advancedSettings: PropTypes.bool.isRequired,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
settings: PropTypes.object.isRequired,
|
||||
hasSettings: PropTypes.bool.isRequired,
|
||||
isWindows: PropTypes.bool.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onInputChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default MediaManagement;
|
559
frontend/src/Settings/MediaManagement/MediaManagement.tsx
Normal file
559
frontend/src/Settings/MediaManagement/MediaManagement.tsx
Normal file
|
@ -0,0 +1,559 @@
|
|||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import AppState from 'App/State/AppState';
|
||||
import Alert from 'Components/Alert';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import Form from 'Components/Form/Form';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import useShowAdvancedSettings from 'Helpers/Hooks/useShowAdvancedSettings';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import RootFolders from 'RootFolder/RootFolders';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||
import {
|
||||
fetchMediaManagementSettings,
|
||||
saveMediaManagementSettings,
|
||||
saveNamingSettings,
|
||||
setMediaManagementSettingsValue,
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
|
||||
import useIsWindows from 'System/useIsWindows';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import isEmpty from 'Utilities/Object/isEmpty';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import Naming from './Naming/Naming';
|
||||
import AddRootFolder from './RootFolder/AddRootFolder';
|
||||
|
||||
const SECTION = 'mediaManagement';
|
||||
|
||||
const episodeTitleRequiredOptions = [
|
||||
{
|
||||
key: 'always',
|
||||
get value() {
|
||||
return translate('Always');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'bulkSeasonReleases',
|
||||
get value() {
|
||||
return translate('OnlyForBulkSeasonReleases');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'never',
|
||||
get value() {
|
||||
return translate('Never');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const rescanAfterRefreshOptions = [
|
||||
{
|
||||
key: 'always',
|
||||
get value() {
|
||||
return translate('Always');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'afterManual',
|
||||
get value() {
|
||||
return translate('AfterManualRefresh');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'never',
|
||||
get value() {
|
||||
return translate('Never');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const downloadPropersAndRepacksOptions = [
|
||||
{
|
||||
key: 'preferAndUpgrade',
|
||||
get value() {
|
||||
return translate('PreferAndUpgrade');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'doNotUpgrade',
|
||||
get value() {
|
||||
return translate('DoNotUpgradeAutomatically');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'doNotPrefer',
|
||||
get value() {
|
||||
return translate('DoNotPrefer');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const fileDateOptions = [
|
||||
{
|
||||
key: 'none',
|
||||
get value() {
|
||||
return translate('None');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'localAirDate',
|
||||
get value() {
|
||||
return translate('LocalAirDate');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'utcAirDate',
|
||||
get value() {
|
||||
return translate('UtcAirDate');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
function MediaManagement() {
|
||||
const dispatch = useDispatch();
|
||||
const showAdvancedSettings = useShowAdvancedSettings();
|
||||
const hasNamingPendingChanges = !isEmpty(
|
||||
useSelector((state: AppState) => state.settings.naming.pendingChanges)
|
||||
);
|
||||
const isWindows = useIsWindows();
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
isSaving,
|
||||
error,
|
||||
settings,
|
||||
hasSettings,
|
||||
hasPendingChanges,
|
||||
validationErrors,
|
||||
validationWarnings,
|
||||
} = useSelector(createSettingsSectionSelector(SECTION));
|
||||
|
||||
const handleSavePress = useCallback(() => {
|
||||
dispatch(saveMediaManagementSettings());
|
||||
dispatch(saveNamingSettings());
|
||||
}, [dispatch]);
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
(change: InputChanged) => {
|
||||
// @ts-expect-error - actions are not typed
|
||||
dispatch(setMediaManagementSettingsValue(change));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchMediaManagementSettings());
|
||||
|
||||
return () => {
|
||||
dispatch(clearPendingChanges({ section: `settings.${SECTION}` }));
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<PageContent title={translate('MediaManagementSettings')}>
|
||||
<SettingsToolbar
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasNamingPendingChanges || hasPendingChanges}
|
||||
onSavePress={handleSavePress}
|
||||
/>
|
||||
|
||||
<PageContentBody>
|
||||
<Naming />
|
||||
|
||||
{isFetching ? (
|
||||
<FieldSet legend={translate('NamingSettings')}>
|
||||
<LoadingIndicator />
|
||||
</FieldSet>
|
||||
) : null}
|
||||
|
||||
{!isFetching && error ? (
|
||||
<FieldSet legend={translate('NamingSettings')}>
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('MediaManagementSettingsLoadError')}
|
||||
</Alert>
|
||||
</FieldSet>
|
||||
) : null}
|
||||
|
||||
{hasSettings && isPopulated && !error ? (
|
||||
<Form
|
||||
id="mediaManagementSettings"
|
||||
validationErrors={validationErrors}
|
||||
validationWarnings={validationWarnings}
|
||||
>
|
||||
{showAdvancedSettings ? (
|
||||
<FieldSet legend={translate('Folders')}>
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('CreateEmptySeriesFolders')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="createEmptySeriesFolders"
|
||||
helpText={translate('CreateEmptySeriesFoldersHelpText')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.createEmptySeriesFolders}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('DeleteEmptyFolders')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="deleteEmptyFolders"
|
||||
helpText={translate('DeleteEmptySeriesFoldersHelpText')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.deleteEmptyFolders}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet>
|
||||
) : null}
|
||||
|
||||
{showAdvancedSettings ? (
|
||||
<FieldSet legend={translate('Importing')}>
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
<FormLabel>{translate('EpisodeTitleRequired')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="episodeTitleRequired"
|
||||
helpText={translate('EpisodeTitleRequiredHelpText')}
|
||||
values={episodeTitleRequiredOptions}
|
||||
onChange={handleInputChange}
|
||||
{...settings.episodeTitleRequired}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('SkipFreeSpaceCheck')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="skipFreeSpaceCheckWhenImporting"
|
||||
helpText={translate('SkipFreeSpaceCheckHelpText')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.skipFreeSpaceCheckWhenImporting}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('MinimumFreeSpace')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
unit="MB"
|
||||
name="minimumFreeSpaceWhenImporting"
|
||||
helpText={translate('MinimumFreeSpaceHelpText')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.minimumFreeSpaceWhenImporting}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>
|
||||
{translate('UseHardlinksInsteadOfCopy')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="copyUsingHardlinks"
|
||||
helpText={translate('CopyUsingHardlinksSeriesHelpText')}
|
||||
helpTextWarning={translate(
|
||||
'CopyUsingHardlinksHelpTextWarning'
|
||||
)}
|
||||
onChange={handleInputChange}
|
||||
{...settings.copyUsingHardlinks}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('ImportUsingScript')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="useScriptImport"
|
||||
helpText={translate('ImportUsingScriptHelpText')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.useScriptImport}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{settings.useScriptImport.value ? (
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ImportScriptPath')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.PATH}
|
||||
includeFiles={true}
|
||||
name="scriptImportPath"
|
||||
helpText={translate('ImportScriptPathHelpText')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.scriptImportPath}
|
||||
/>
|
||||
</FormGroup>
|
||||
) : null}
|
||||
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>{translate('ImportExtraFiles')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="importExtraFiles"
|
||||
helpText={translate('ImportExtraFilesEpisodeHelpText')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.importExtraFiles}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{settings.importExtraFiles.value ? (
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ImportExtraFiles')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="extraFileExtensions"
|
||||
helpTexts={[
|
||||
translate('ExtraFileExtensionsHelpText'),
|
||||
translate('ExtraFileExtensionsHelpTextsExamples'),
|
||||
]}
|
||||
onChange={handleInputChange}
|
||||
{...settings.extraFileExtensions}
|
||||
/>
|
||||
</FormGroup>
|
||||
) : null}
|
||||
</FieldSet>
|
||||
) : null}
|
||||
|
||||
<FieldSet legend={translate('FileManagement')}>
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>{translate('UnmonitorDeletedEpisodes')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="autoUnmonitorPreviouslyDownloadedEpisodes"
|
||||
helpText={translate('UnmonitorDeletedEpisodesHelpText')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.autoUnmonitorPreviouslyDownloadedEpisodes}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('DownloadPropersAndRepacks')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="downloadPropersAndRepacks"
|
||||
helpTexts={[
|
||||
translate('DownloadPropersAndRepacksHelpText'),
|
||||
translate('DownloadPropersAndRepacksHelpTextCustomFormat'),
|
||||
]}
|
||||
helpTextWarning={
|
||||
settings.downloadPropersAndRepacks.value === 'doNotPrefer'
|
||||
? translate('DownloadPropersAndRepacksHelpTextWarning')
|
||||
: undefined
|
||||
}
|
||||
values={downloadPropersAndRepacksOptions}
|
||||
onChange={handleInputChange}
|
||||
{...settings.downloadPropersAndRepacks}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('AnalyseVideoFiles')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableMediaInfo"
|
||||
helpText={translate('AnalyseVideoFilesHelpText')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.enableMediaInfo}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>
|
||||
{translate('RescanSeriesFolderAfterRefresh')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="rescanAfterRefresh"
|
||||
helpText={translate('RescanAfterRefreshSeriesHelpText')}
|
||||
helpTextWarning={translate(
|
||||
'RescanAfterRefreshHelpTextWarning'
|
||||
)}
|
||||
values={rescanAfterRefreshOptions}
|
||||
onChange={handleInputChange}
|
||||
{...settings.rescanAfterRefresh}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ChangeFileDate')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="fileDate"
|
||||
helpText={translate('ChangeFileDateHelpText')}
|
||||
values={fileDateOptions}
|
||||
onChange={handleInputChange}
|
||||
{...settings.fileDate}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('RecyclingBin')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.PATH}
|
||||
name="recycleBin"
|
||||
helpText={translate('RecyclingBinHelpText')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.recycleBin}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('RecyclingBinCleanup')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="recycleBinCleanupDays"
|
||||
helpText={translate('RecyclingBinCleanupHelpText')}
|
||||
helpTextWarning={translate(
|
||||
'RecyclingBinCleanupHelpTextWarning'
|
||||
)}
|
||||
min={0}
|
||||
onChange={handleInputChange}
|
||||
{...settings.recycleBinCleanupDays}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet>
|
||||
|
||||
{showAdvancedSettings && !isWindows ? (
|
||||
<FieldSet legend={translate('Permissions')}>
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('SetPermissions')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="setPermissionsLinux"
|
||||
helpText={translate('SetPermissionsLinuxHelpText')}
|
||||
helpTextWarning={translate(
|
||||
'SetPermissionsLinuxHelpTextWarning'
|
||||
)}
|
||||
onChange={handleInputChange}
|
||||
{...settings.setPermissionsLinux}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ChmodFolder')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.UMASK}
|
||||
name="chmodFolder"
|
||||
helpText={translate('ChmodFolderHelpText')}
|
||||
helpTextWarning={translate('ChmodFolderHelpTextWarning')}
|
||||
onChange={handleInputChange}
|
||||
{...settings.chmodFolder}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ChownGroup')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="chownGroup"
|
||||
helpText={translate('ChownGroupHelpText')}
|
||||
helpTextWarning={translate('ChownGroupHelpTextWarning')}
|
||||
values={fileDateOptions}
|
||||
onChange={handleInputChange}
|
||||
{...settings.chownGroup}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet>
|
||||
) : null}
|
||||
</Form>
|
||||
) : null}
|
||||
|
||||
<FieldSet legend={translate('RootFolders')}>
|
||||
<RootFolders />
|
||||
<AddRootFolder />
|
||||
</FieldSet>
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default MediaManagement;
|
|
@ -1,86 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||
import { fetchMediaManagementSettings, saveMediaManagementSettings, saveNamingSettings, setMediaManagementSettingsValue } from 'Store/Actions/settingsActions';
|
||||
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
|
||||
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
|
||||
import MediaManagement from './MediaManagement';
|
||||
|
||||
const SECTION = 'mediaManagement';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.settings.advancedSettings,
|
||||
(state) => state.settings.naming,
|
||||
createSettingsSectionSelector(SECTION),
|
||||
createSystemStatusSelector(),
|
||||
(advancedSettings, namingSettings, sectionSettings, systemStatus) => {
|
||||
return {
|
||||
advancedSettings,
|
||||
...sectionSettings,
|
||||
hasPendingChanges: !_.isEmpty(namingSettings.pendingChanges) || sectionSettings.hasPendingChanges,
|
||||
isWindows: systemStatus.isWindows
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchMediaManagementSettings,
|
||||
setMediaManagementSettingsValue,
|
||||
saveMediaManagementSettings,
|
||||
saveNamingSettings,
|
||||
clearPendingChanges
|
||||
};
|
||||
|
||||
class MediaManagementConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchMediaManagementSettings();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.clearPendingChanges({ section: `settings.${SECTION}` });
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onInputChange = ({ name, value }) => {
|
||||
this.props.setMediaManagementSettingsValue({ name, value });
|
||||
};
|
||||
|
||||
onSavePress = () => {
|
||||
this.props.saveMediaManagementSettings();
|
||||
this.props.saveNamingSettings();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<MediaManagement
|
||||
onInputChange={this.onInputChange}
|
||||
onSavePress={this.onSavePress}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MediaManagementConnector.propTypes = {
|
||||
fetchMediaManagementSettings: PropTypes.func.isRequired,
|
||||
setMediaManagementSettingsValue: PropTypes.func.isRequired,
|
||||
saveMediaManagementSettings: PropTypes.func.isRequired,
|
||||
saveNamingSettings: PropTypes.func.isRequired,
|
||||
clearPendingChanges: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(MediaManagementConnector);
|
8
frontend/src/System/useIsWindows.ts
Normal file
8
frontend/src/System/useIsWindows.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { useSelector } from 'react-redux';
|
||||
import AppState from 'App/State/AppState';
|
||||
|
||||
function useIsWindows() {
|
||||
return useSelector((state: AppState) => state.system.status.item.isWindows);
|
||||
}
|
||||
|
||||
export default useIsWindows;
|
22
frontend/src/typings/Settings/MediaManagement.ts
Normal file
22
frontend/src/typings/Settings/MediaManagement.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
export default interface MediaManagement {
|
||||
autoUnmonitorPreviouslyDownloadedEpisodes: boolean;
|
||||
recycleBin: string;
|
||||
recycleBinCleanupDays: number;
|
||||
downloadPropersAndRepacks: string;
|
||||
createEmptySeriesFolders: boolean;
|
||||
deleteEmptyFolders: boolean;
|
||||
fileDate: string;
|
||||
rescanAfterRefresh: string;
|
||||
setPermissionsLinux: boolean;
|
||||
chmodFolder: string;
|
||||
chownGroup: string;
|
||||
episodeTitleRequired: string;
|
||||
skipFreeSpaceCheckWhenImporting: boolean;
|
||||
minimumFreeSpaceWhenImporting: number;
|
||||
copyUsingHardlinks: boolean;
|
||||
useScriptImport: boolean;
|
||||
scriptImportPath: string;
|
||||
importExtraFiles: boolean;
|
||||
extraFileExtensions: string;
|
||||
enableMediaInfo: boolean;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue