New: Import list exclusion pagination and bulk removal

(cherry picked from commit 428569106499b5e3a463f1990ae2996d1ae4ab49)

Persist page size for Import List Exclusions

(cherry picked from commit e81bb3b993adac705fd61dc9e281b040ca2338f5)

Clear pending changes for edit import list exclusions on modal close

(cherry picked from commit 7b87de2e93c2aa499cff224f84253ba944bb58d4)

Fixed actions column width for import list exclusions

(cherry picked from commit d691ad8e12ea4f2bc77f0b551c17d22d91c4ba22)
pull/10287/head
The Dark 11 months ago committed by Qstick
parent b1a7652753
commit b6d9c73a17

@ -18,7 +18,10 @@ export interface AppSectionSaveState {
} }
export interface PagedAppSectionState { export interface PagedAppSectionState {
page: number;
pageSize: number; pageSize: number;
totalPages: number;
totalRecords?: number;
} }
export interface AppSectionFilterState<T> { export interface AppSectionFilterState<T> {
@ -38,6 +41,7 @@ export interface AppSectionItemState<T> {
isFetching: boolean; isFetching: boolean;
isPopulated: boolean; isPopulated: boolean;
error: Error; error: Error;
pendingChanges: Partial<T>;
item: T; item: T;
} }

@ -3,10 +3,12 @@ import AppSectionState, {
AppSectionItemState, AppSectionItemState,
AppSectionSaveState, AppSectionSaveState,
AppSectionSchemaState, AppSectionSchemaState,
PagedAppSectionState,
} from 'App/State/AppSectionState'; } from 'App/State/AppSectionState';
import Language from 'Language/Language'; import Language from 'Language/Language';
import DownloadClient from 'typings/DownloadClient'; import DownloadClient from 'typings/DownloadClient';
import ImportList from 'typings/ImportList'; import ImportList from 'typings/ImportList';
import ImportListExclusion from 'typings/ImportListExclusion';
import ImportListOptionsSettings from 'typings/ImportListOptionsSettings'; import ImportListOptionsSettings from 'typings/ImportListOptionsSettings';
import Indexer from 'typings/Indexer'; import Indexer from 'typings/Indexer';
import IndexerFlag from 'typings/IndexerFlag'; import IndexerFlag from 'typings/IndexerFlag';
@ -41,6 +43,14 @@ export interface ImportListOptionsSettingsAppState
extends AppSectionItemState<ImportListOptionsSettings>, extends AppSectionItemState<ImportListOptionsSettings>,
AppSectionSaveState {} AppSectionSaveState {}
export interface ImportListExclusionsSettingsAppState
extends AppSectionState<ImportListExclusion>,
AppSectionSaveState,
PagedAppSectionState,
AppSectionDeleteState {
pendingChanges: Partial<ImportListExclusion>;
}
export type IndexerFlagSettingsAppState = AppSectionState<IndexerFlag>; export type IndexerFlagSettingsAppState = AppSectionState<IndexerFlag>;
export type LanguageSettingsAppState = AppSectionState<Language>; export type LanguageSettingsAppState = AppSectionState<Language>;
export type UiSettingsAppState = AppSectionItemState<UiSettings>; export type UiSettingsAppState = AppSectionItemState<UiSettings>;
@ -48,6 +58,7 @@ export type UiSettingsAppState = AppSectionItemState<UiSettings>;
interface SettingsAppState { interface SettingsAppState {
advancedSettings: boolean; advancedSettings: boolean;
downloadClients: DownloadClientAppState; downloadClients: DownloadClientAppState;
importListExclusions: ImportListExclusionsSettingsAppState;
importListOptions: ImportListOptionsSettingsAppState; importListOptions: ImportListOptionsSettingsAppState;
importLists: ImportListAppState; importLists: ImportListAppState;
indexerFlags: IndexerFlagSettingsAppState; indexerFlags: IndexerFlagSettingsAppState;

@ -5,6 +5,7 @@ type PropertyFunction<T> = () => T;
interface Column { interface Column {
name: string; name: string;
label: string | PropertyFunction<string> | React.ReactNode; label: string | PropertyFunction<string> | React.ReactNode;
className?: string;
columnLabel?: string; columnLabel?: string;
isSortable?: boolean; isSortable?: boolean;
isVisible: boolean; isVisible: boolean;

@ -66,7 +66,9 @@ function Table(props) {
columns.map((column) => { columns.map((column) => {
const { const {
name, name,
isVisible isVisible,
isSortable,
...otherColumnProps
} = column; } = column;
if (!isVisible) { if (!isVisible) {
@ -84,6 +86,7 @@ function Table(props) {
name={name} name={name}
isSortable={false} isSortable={false}
{...otherProps} {...otherProps}
{...otherColumnProps}
> >
<TableOptionsModalWrapper <TableOptionsModalWrapper
columns={columns} columns={columns}

@ -0,0 +1,54 @@
import { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
interface PagingOptions {
page: number;
totalPages: number;
gotoPage: ({ page }: { page: number }) => void;
}
function usePaging(options: PagingOptions) {
const { page, totalPages, gotoPage } = options;
const dispatch = useDispatch();
const handleFirstPagePress = useCallback(() => {
dispatch(gotoPage({ page: 1 }));
}, [dispatch, gotoPage]);
const handlePreviousPagePress = useCallback(() => {
dispatch(gotoPage({ page: Math.max(page - 1, 1) }));
}, [page, dispatch, gotoPage]);
const handleNextPagePress = useCallback(() => {
dispatch(gotoPage({ page: Math.min(page + 1, totalPages) }));
}, [page, totalPages, dispatch, gotoPage]);
const handleLastPagePress = useCallback(() => {
dispatch(gotoPage({ page: totalPages }));
}, [totalPages, dispatch, gotoPage]);
const handlePageSelect = useCallback(
(page: number) => {
dispatch(gotoPage({ page }));
},
[dispatch, gotoPage]
);
return useMemo(() => {
return {
handleFirstPagePress,
handlePreviousPagePress,
handleNextPagePress,
handleLastPagePress,
handlePageSelect,
};
}, [
handleFirstPagePress,
handlePreviousPagePress,
handleNextPagePress,
handleLastPagePress,
handlePageSelect,
]);
}
export default usePaging;

@ -0,0 +1,9 @@
import { useHistory } from 'react-router-dom';
function useCurrentPage() {
const history = useHistory();
return history.action === 'POP';
}
export default useCurrentPage;

@ -1,27 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import Modal from 'Components/Modal/Modal';
import { sizes } from 'Helpers/Props';
import EditImportListExclusionModalContentConnector from './EditImportListExclusionModalContentConnector';
function EditImportListExclusionModal({ isOpen, onModalClose, ...otherProps }) {
return (
<Modal
size={sizes.MEDIUM}
isOpen={isOpen}
onModalClose={onModalClose}
>
<EditImportListExclusionModalContentConnector
{...otherProps}
onModalClose={onModalClose}
/>
</Modal>
);
}
EditImportListExclusionModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default EditImportListExclusionModal;

@ -0,0 +1,41 @@
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import Modal from 'Components/Modal/Modal';
import { sizes } from 'Helpers/Props';
import { clearPendingChanges } from 'Store/Actions/baseActions';
import EditImportListExclusionModalContent from './EditImportListExclusionModalContent';
interface EditImportListExclusionModalProps {
id?: number;
isOpen: boolean;
onModalClose: () => void;
onDeleteImportListExclusionPress?: () => void;
}
function EditImportListExclusionModal(
props: EditImportListExclusionModalProps
) {
const { isOpen, onModalClose, ...otherProps } = props;
const dispatch = useDispatch();
const onModalClosePress = useCallback(() => {
dispatch(
clearPendingChanges({
section: 'settings.importListExclusions',
})
);
onModalClose();
}, [dispatch, onModalClose]);
return (
<Modal size={sizes.MEDIUM} isOpen={isOpen} onModalClose={onModalClosePress}>
<EditImportListExclusionModalContent
{...otherProps}
onModalClose={onModalClosePress}
/>
</Modal>
);
}
export default EditImportListExclusionModal;

@ -1,43 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { clearPendingChanges } from 'Store/Actions/baseActions';
import EditImportListExclusionModal from './EditImportListExclusionModal';
function mapStateToProps() {
return {};
}
const mapDispatchToProps = {
clearPendingChanges
};
class EditImportListExclusionModalConnector extends Component {
//
// Listeners
onModalClose = () => {
this.props.clearPendingChanges({ section: 'settings.importListExclusions' });
this.props.onModalClose();
};
//
// Render
render() {
return (
<EditImportListExclusionModal
{...this.props}
onModalClose={this.onModalClose}
/>
);
}
}
EditImportListExclusionModalConnector.propTypes = {
onModalClose: PropTypes.func.isRequired,
clearPendingChanges: PropTypes.func.isRequired
};
export default connect(mapStateToProps, mapDispatchToProps)(EditImportListExclusionModalConnector);

@ -1,147 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import Alert from 'Components/Alert';
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 Button from 'Components/Link/Button';
import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
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 './EditImportListExclusionModalContent.css';
function EditImportListExclusionModalContent(props) {
const {
id,
isFetching,
error,
isSaving,
saveError,
item,
onInputChange,
onSavePress,
onModalClose,
onDeleteImportListExclusionPress,
...otherProps
} = props;
const {
movieTitle = '',
tmdbId,
movieYear
} = item;
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{id ? translate('EditImportListExclusion') : translate('AddImportListExclusion')}
</ModalHeader>
<ModalBody className={styles.body}>
{
isFetching &&
<LoadingIndicator />
}
{
!isFetching && !!error &&
<Alert kind={kinds.DANGER}>
{translate('AddImportListExclusionError')}
</Alert>
}
{
!isFetching && !error &&
<Form
{...otherProps}
>
<FormGroup>
<FormLabel>{translate('TMDBId')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="tmdbId"
helpText={translate('TmdbIdHelpText')}
{...tmdbId}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('MovieTitle')}</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="movieTitle"
helpText={translate('MovieTitleHelpText')}
{...movieTitle}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('MovieYear')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="movieYear"
helpText={translate('MovieYearHelpText')}
{...movieYear}
onChange={onInputChange}
/>
</FormGroup>
</Form>
}
</ModalBody>
<ModalFooter>
{
id &&
<Button
className={styles.deleteButton}
kind={kinds.DANGER}
onPress={onDeleteImportListExclusionPress}
>
{translate('Delete')}
</Button>
}
<Button
onPress={onModalClose}
>
{translate('Cancel')}
</Button>
<SpinnerErrorButton
isSpinning={isSaving}
error={saveError}
onPress={onSavePress}
>
{translate('Save')}
</SpinnerErrorButton>
</ModalFooter>
</ModalContent>
);
}
EditImportListExclusionModalContent.propTypes = {
id: PropTypes.number,
isFetching: PropTypes.bool.isRequired,
error: PropTypes.object,
isSaving: PropTypes.bool.isRequired,
saveError: PropTypes.object,
item: PropTypes.object.isRequired,
onInputChange: PropTypes.func.isRequired,
onSavePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired,
onDeleteImportListExclusionPress: PropTypes.func
};
export default EditImportListExclusionModalContent;

@ -0,0 +1,201 @@
import React, { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import Alert from 'Components/Alert';
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 Button from 'Components/Link/Button';
import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
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 usePrevious from 'Helpers/Hooks/usePrevious';
import { inputTypes, kinds } from 'Helpers/Props';
import {
saveImportListExclusion,
setImportListExclusionValue,
} from 'Store/Actions/settingsActions';
import selectSettings from 'Store/Selectors/selectSettings';
import ImportListExclusion from 'typings/ImportListExclusion';
import { PendingSection } from 'typings/pending';
import translate from 'Utilities/String/translate';
import styles from './EditImportListExclusionModalContent.css';
const newImportListExclusion = {
movieTitle: '',
movieYear: 0,
tmdbId: 0,
};
interface EditImportListExclusionModalContentProps {
id?: number;
onModalClose: () => void;
onDeleteImportListExclusionPress?: () => void;
}
function createImportListExclusionSelector(id?: number) {
return createSelector(
(state: AppState) => state.settings.importListExclusions,
(importListExclusions) => {
const { isFetching, error, isSaving, saveError, pendingChanges, items } =
importListExclusions;
const mapping = id
? items.find((i) => i.id === id)
: newImportListExclusion;
const settings = selectSettings(mapping, pendingChanges, saveError);
return {
id,
isFetching,
error,
isSaving,
saveError,
item: settings.settings as PendingSection<ImportListExclusion>,
...settings,
};
}
);
}
function EditImportListExclusionModalContent(
props: EditImportListExclusionModalContentProps
) {
const { id, onModalClose, onDeleteImportListExclusionPress } = props;
const dispatch = useDispatch();
const dispatchSetImportListExclusionValue = (payload: {
name: string;
value: string | number;
}) => {
// @ts-expect-error 'setImportListExclusionValue' isn't typed yet
dispatch(setImportListExclusionValue(payload));
};
const { isFetching, isSaving, item, error, saveError, ...otherProps } =
useSelector(createImportListExclusionSelector(props.id));
const previousIsSaving = usePrevious(isSaving);
const { movieTitle, movieYear, tmdbId } = item;
useEffect(() => {
if (!id) {
Object.keys(newImportListExclusion).forEach((name) => {
dispatchSetImportListExclusionValue({
name,
value:
newImportListExclusion[name as keyof typeof newImportListExclusion],
});
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (previousIsSaving && !isSaving && !saveError) {
onModalClose();
}
});
const onSavePress = useCallback(() => {
dispatch(saveImportListExclusion({ id }));
}, [dispatch, id]);
const onInputChange = useCallback(
(payload: { name: string; value: string | number }) => {
// @ts-expect-error 'setImportListExclusionValue' isn't typed yet
dispatch(setImportListExclusionValue(payload));
},
[dispatch]
);
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{id
? translate('EditImportListExclusion')
: translate('AddImportListExclusion')}
</ModalHeader>
<ModalBody className={styles.body}>
{isFetching && <LoadingIndicator />}
{!isFetching && !!error && (
<Alert kind={kinds.DANGER}>
{translate('AddImportListExclusionError')}
</Alert>
)}
{!isFetching && !error && (
<Form {...otherProps}>
<FormGroup>
<FormLabel>{translate('TMDBId')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="tmdbId"
helpText={translate('TmdbIdExcludeHelpText')}
{...tmdbId}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('Title')}</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="movieTitle"
helpText={translate('MovieTitleToExcludeHelpText')}
{...movieTitle}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('Year')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="movieYear"
helpText={translate('MovieYearToExcludeHelpText')}
{...movieYear}
onChange={onInputChange}
/>
</FormGroup>
</Form>
)}
</ModalBody>
<ModalFooter>
{id && (
<Button
className={styles.deleteButton}
kind={kinds.DANGER}
onPress={onDeleteImportListExclusionPress}
>
{translate('Delete')}
</Button>
)}
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
<SpinnerErrorButton
isSpinning={isSaving}
error={saveError}
onPress={onSavePress}
>
{translate('Save')}
</SpinnerErrorButton>
</ModalFooter>
</ModalContent>
);
}
export default EditImportListExclusionModalContent;

@ -1,118 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { saveImportListExclusion, setImportListExclusionValue } from 'Store/Actions/settingsActions';
import selectSettings from 'Store/Selectors/selectSettings';
import EditImportListExclusionModalContent from './EditImportListExclusionModalContent';
const newImportListExclusion = {
movieTitle: '',
tmdbId: 0,
movieYear: 0
};
function createImportListExclusionSelector() {
return createSelector(
(state, { id }) => id,
(state) => state.settings.importListExclusions,
(id, importListExclusions) => {
const {
isFetching,
error,
isSaving,
saveError,
pendingChanges,
items
} = importListExclusions;
const mapping = id ? items.find((i) => i.id === id) : newImportListExclusion;
const settings = selectSettings(mapping, pendingChanges, saveError);
return {
id,
isFetching,
error,
isSaving,
saveError,
item: settings.settings,
...settings
};
}
);
}
function createMapStateToProps() {
return createSelector(
createImportListExclusionSelector(),
(importListExclusion) => {
return {
...importListExclusion
};
}
);
}
const mapDispatchToProps = {
setImportListExclusionValue,
saveImportListExclusion
};
class EditImportListExclusionModalContentConnector extends Component {
//
// Lifecycle
componentDidMount() {
if (!this.props.id) {
Object.keys(newImportListExclusion).forEach((name) => {
this.props.setImportListExclusionValue({
name,
value: newImportListExclusion[name]
});
});
}
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
this.props.onModalClose();
}
}
//
// Listeners
onInputChange = ({ name, value }) => {
this.props.setImportListExclusionValue({ name, value });
};
onSavePress = () => {
this.props.saveImportListExclusion({ id: this.props.id });
};
//
// Render
render() {
return (
<EditImportListExclusionModalContent
{...this.props}
onSavePress={this.onSavePress}
onInputChange={this.onInputChange}
/>
);
}
}
EditImportListExclusionModalContentConnector.propTypes = {
id: PropTypes.number,
isSaving: PropTypes.bool.isRequired,
saveError: PropTypes.object,
item: PropTypes.object.isRequired,
setImportListExclusionValue: PropTypes.func.isRequired,
saveImportListExclusion: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(EditImportListExclusionModalContentConnector);

@ -1,26 +0,0 @@
.importListExclusion {
display: flex;
align-items: stretch;
margin-bottom: 10px;
height: 30px;
border-bottom: 1px solid var(--borderColor);
line-height: 30px;
}
.movieTitle {
@add-mixin truncate;
flex: 0 1 600px;
}
.tmdbId,
.movieYear {
flex: 0 0 70px;
}
.actions {
display: flex;
justify-content: flex-end;
flex: 1 0 auto;
padding-right: 10px;
}

@ -1,115 +0,0 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector';
import styles from './ImportListExclusion.css';
class ImportListExclusion extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isEditImportListExclusionModalOpen: false,
isDeleteImportListExclusionModalOpen: false
};
}
//
// Listeners
onEditImportListExclusionPress = () => {
this.setState({ isEditImportListExclusionModalOpen: true });
};
onEditImportListExclusionModalClose = () => {
this.setState({ isEditImportListExclusionModalOpen: false });
};
onDeleteImportListExclusionPress = () => {
this.setState({
isEditImportListExclusionModalOpen: false,
isDeleteImportListExclusionModalOpen: true
});
};
onDeleteImportListExclusionModalClose = () => {
this.setState({ isDeleteImportListExclusionModalOpen: false });
};
onConfirmDeleteImportListExclusion = () => {
this.props.onConfirmDeleteImportListExclusion(this.props.id);
};
//
// Render
render() {
const {
id,
movieTitle,
tmdbId,
movieYear
} = this.props;
return (
<div
className={classNames(
styles.importListExclusion
)}
>
<div className={styles.tmdbId}>{tmdbId}</div>
<div className={styles.movieTitle} title={movieTitle}>{movieTitle}</div>
<div className={styles.movieYear}>{movieYear}</div>
<div className={styles.actions}>
<Link
onPress={this.onEditImportListExclusionPress}
>
<Icon name={icons.EDIT} />
</Link>
</div>
<EditImportListExclusionModalConnector
id={id}
isOpen={this.state.isEditImportListExclusionModalOpen}
onModalClose={this.onEditImportListExclusionModalClose}
onDeleteImportListExclusionPress={this.onDeleteImportListExclusionPress}
/>
<ConfirmModal
isOpen={this.state.isDeleteImportListExclusionModalOpen}
kind={kinds.DANGER}
title={translate('DeleteImportListExclusion')}
message={translate('DeleteImportListExclusionMessageText')}
confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteImportListExclusion}
onCancel={this.onDeleteImportListExclusionModalClose}
/>
</div>
);
}
}
ImportListExclusion.propTypes = {
id: PropTypes.number.isRequired,
movieTitle: PropTypes.string.isRequired,
tmdbId: PropTypes.number.isRequired,
movieYear: PropTypes.number.isRequired,
onConfirmDeleteImportListExclusion: PropTypes.func.isRequired
};
ImportListExclusion.defaultProps = {
// The drag preview will not connect the drag handle.
connectDragSource: (node) => node
};
export default ImportListExclusion;

@ -0,0 +1,6 @@
.actions {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 35px;
white-space: nowrap;
}

@ -2,10 +2,6 @@
// Please do not change this file! // Please do not change this file!
interface CssExports { interface CssExports {
'actions': string; 'actions': string;
'importListExclusion': string;
'movieTitle': string;
'movieYear': string;
'tmdbId': string;
} }
export const cssExports: CssExports; export const cssExports: CssExports;
export default cssExports; export default cssExports;

@ -0,0 +1,83 @@
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import IconButton from 'Components/Link/IconButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import TableRow from 'Components/Table/TableRow';
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
import { icons, kinds } from 'Helpers/Props';
import { deleteImportListExclusion } from 'Store/Actions/Settings/importListExclusions';
import ImportListExclusion from 'typings/ImportListExclusion';
import { SelectStateInputProps } from 'typings/props';
import translate from 'Utilities/String/translate';
import EditImportListExclusionModal from './EditImportListExclusionModal';
import styles from './ImportListExclusionRow.css';
interface ImportListExclusionRowProps extends ImportListExclusion {
isSelected: boolean;
onSelectedChange: (options: SelectStateInputProps) => void;
}
function ImportListExclusionRow(props: ImportListExclusionRowProps) {
const { id, tmdbId, movieTitle, movieYear, isSelected, onSelectedChange } =
props;
const dispatch = useDispatch();
const [
isEditImportListExclusionModalOpen,
setEditImportListExclusionModalOpen,
setEditImportListExclusionModalClosed,
] = useModalOpenState(false);
const [
isDeleteImportListExclusionModalOpen,
setDeleteImportListExclusionModalOpen,
setDeleteImportListExclusionModalClosed,
] = useModalOpenState(false);
const handleDeletePress = useCallback(() => {
dispatch(deleteImportListExclusion({ id }));
}, [id, dispatch]);
return (
<TableRow>
<TableSelectCell
id={id}
isSelected={isSelected}
onSelectedChange={onSelectedChange}
/>
<TableRowCell>{tmdbId}</TableRowCell>
<TableRowCell>{movieTitle}</TableRowCell>
<TableRowCell>{movieYear}</TableRowCell>
<TableRowCell className={styles.actions}>
<IconButton
name={icons.EDIT}
onPress={setEditImportListExclusionModalOpen}
/>
</TableRowCell>
<EditImportListExclusionModal
id={id}
isOpen={isEditImportListExclusionModalOpen}
onModalClose={setEditImportListExclusionModalClosed}
onDeleteImportListExclusionPress={setDeleteImportListExclusionModalOpen}
/>
<ConfirmModal
isOpen={isDeleteImportListExclusionModalOpen}
kind={kinds.DANGER}
title={translate('DeleteImportListExclusion')}
message={translate('DeleteImportListExclusionMessageText')}
confirmLabel={translate('Delete')}
onConfirm={handleDeletePress}
onCancel={setDeleteImportListExclusionModalClosed}
/>
</TableRow>
);
}
export default ImportListExclusionRow;

@ -1,24 +1,6 @@
.importListExclusionsHeader { .actions {
display: flex; composes: headerCell from '~Components/Table/TableHeaderCell.css';
margin-bottom: 10px;
font-weight: bold;
}
.title {
flex: 0 1 600px;
}
.tmdbId,
.movieYear {
flex: 0 0 70px;
}
.addImportListExclusion {
display: flex;
justify-content: flex-end;
padding-right: 10px;
}
.addButton { width: 35px;
text-align: center; white-space: nowrap;
} }

@ -1,12 +1,7 @@
// This file is automatically generated. // This file is automatically generated.
// Please do not change this file! // Please do not change this file!
interface CssExports { interface CssExports {
'addButton': string; 'actions': string;
'addImportListExclusion': string;
'importListExclusionsHeader': string;
'movieYear': string;
'title': string;
'tmdbId': string;
} }
export const cssExports: CssExports; export const cssExports: CssExports;
export default cssExports; export default cssExports;

@ -1,108 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import FieldSet from 'Components/FieldSet';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import PageSectionContent from 'Components/Page/PageSectionContent';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector';
import ImportListExclusion from './ImportListExclusion';
import styles from './ImportListExclusions.css';
class ImportListExclusions extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isAddImportListExclusionModalOpen: false
};
}
//
// Listeners
onAddImportListExclusionPress = () => {
this.setState({ isAddImportListExclusionModalOpen: true });
};
onModalClose = () => {
this.setState({ isAddImportListExclusionModalOpen: false });
};
//
// Render
render() {
const {
items,
onConfirmDeleteImportListExclusion,
...otherProps
} = this.props;
return (
<FieldSet legend={translate('ImportListExclusions')}>
<PageSectionContent
errorMessage={translate('ImportListExclusionsLoadError')}
{...otherProps}
>
<div className={styles.importListExclusionsHeader}>
<div className={styles.tmdbId}>
{translate('TMDBId')}
</div>
<div className={styles.title}>
{translate('Title')}
</div>
<div className={styles.movieYear}>
{translate('Year')}
</div>
</div>
<div>
{
items.map((item, index) => {
return (
<ImportListExclusion
key={item.id}
{...item}
{...otherProps}
index={index}
onConfirmDeleteImportListExclusion={onConfirmDeleteImportListExclusion}
/>
);
})
}
</div>
<div className={styles.addImportListExclusion}>
<Link
className={styles.addButton}
onPress={this.onAddImportListExclusionPress}
>
<Icon name={icons.ADD} />
</Link>
</div>
<EditImportListExclusionModalConnector
isOpen={this.state.isAddImportListExclusionModalOpen}
onModalClose={this.onModalClose}
/>
</PageSectionContent>
</FieldSet>
);
}
}
ImportListExclusions.propTypes = {
isFetching: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
onConfirmDeleteImportListExclusion: PropTypes.func.isRequired
};
export default ImportListExclusions;

@ -0,0 +1,309 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import FieldSet from 'Components/FieldSet';
import IconButton from 'Components/Link/IconButton';
import SpinnerButton from 'Components/Link/SpinnerButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import PageSectionContent from 'Components/Page/PageSectionContent';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import Column from 'Components/Table/Column';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import TablePager from 'Components/Table/TablePager';
import TableRow from 'Components/Table/TableRow';
import usePaging from 'Components/Table/usePaging';
import useCurrentPage from 'Helpers/Hooks/useCurrentPage';
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
import usePrevious from 'Helpers/Hooks/usePrevious';
import useSelectState from 'Helpers/Hooks/useSelectState';
import { icons, kinds } from 'Helpers/Props';
import {
bulkDeleteImportListExclusions,
clearImportListExclusions,
fetchImportListExclusions,
gotoImportListExclusionPage,
setImportListExclusionSort,
setImportListExclusionTableOption,
} from 'Store/Actions/Settings/importListExclusions';
import { CheckInputChanged } from 'typings/inputs';
import { SelectStateInputProps } from 'typings/props';
import { TableOptionsChangePayload } from 'typings/Table';
import {
registerPagePopulator,
unregisterPagePopulator,
} from 'Utilities/pagePopulator';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import EditImportListExclusionModal from './EditImportListExclusionModal';
import ImportListExclusionRow from './ImportListExclusionRow';
import styles from './ImportListExclusions.css';
const COLUMNS: Column[] = [
{
name: 'tmdbid',
label: () => translate('TMDBId'),
isVisible: true,
isSortable: true,
},
{
name: 'movieTitle',
label: () => translate('Title'),
isVisible: true,
isSortable: true,
},
{
name: 'movieYear',
label: () => translate('Year'),
isVisible: true,
isSortable: true,
},
{
className: styles.actions,
name: 'actions',
label: '',
isVisible: true,
isSortable: false,
},
];
function createImportListExclusionsSelector() {
return createSelector(
(state: AppState) => state.settings.importListExclusions,
(importListExclusions) => {
return {
...importListExclusions,
};
}
);
}
function ImportListExclusions() {
const requestCurrentPage = useCurrentPage();
const {
isFetching,
isPopulated,
items,
pageSize,
sortKey,
error,
sortDirection,
page,
totalPages,
totalRecords,
isDeleting,
deleteError,
} = useSelector(createImportListExclusionsSelector());
const dispatch = useDispatch();
const [isConfirmDeleteModalOpen, setIsConfirmDeleteModalOpen] =
useState(false);
const previousIsDeleting = usePrevious(isDeleting);
const [selectState, setSelectState] = useSelectState();
const { allSelected, allUnselected, selectedState } = selectState;
const selectedIds = useMemo(() => {
return getSelectedIds(selectedState);
}, [selectedState]);
const handleSelectAllChange = useCallback(
({ value }: CheckInputChanged) => {
setSelectState({ type: value ? 'selectAll' : 'unselectAll', items });
},
[items, setSelectState]
);
const handleSelectedChange = useCallback(
({ id, value, shiftKey = false }: SelectStateInputProps) => {
setSelectState({
type: 'toggleSelected',
items,
id,
isSelected: value,
shiftKey,
});
},
[items, setSelectState]
);
const handleDeleteSelectedPress = useCallback(() => {
setIsConfirmDeleteModalOpen(true);
}, [setIsConfirmDeleteModalOpen]);
const handleDeleteSelectedConfirmed = useCallback(() => {
dispatch(bulkDeleteImportListExclusions({ ids: selectedIds }));
setIsConfirmDeleteModalOpen(false);
}, [selectedIds, setIsConfirmDeleteModalOpen, dispatch]);
const handleConfirmDeleteModalClose = useCallback(() => {
setIsConfirmDeleteModalOpen(false);
}, [setIsConfirmDeleteModalOpen]);
const {
handleFirstPagePress,
handlePreviousPagePress,
handleNextPagePress,
handleLastPagePress,
handlePageSelect,
} = usePaging({
page,
totalPages,
gotoPage: gotoImportListExclusionPage,
});
const handleSortPress = useCallback(
(sortKey: { sortKey: string }) => {
dispatch(setImportListExclusionSort({ sortKey }));
},
[dispatch]
);
const handleTableOptionChange = useCallback(
(payload: TableOptionsChangePayload) => {
dispatch(setImportListExclusionTableOption(payload));
if (payload.pageSize) {
dispatch(gotoImportListExclusionPage({ page: 1 }));
}
},
[dispatch]
);
useEffect(() => {
if (requestCurrentPage) {
dispatch(fetchImportListExclusions());
} else {
dispatch(gotoImportListExclusionPage({ page: 1 }));
}
return () => {
dispatch(clearImportListExclusions());
};
}, [requestCurrentPage, dispatch]);
useEffect(() => {
const repopulate = () => {
dispatch(fetchImportListExclusions());
};
registerPagePopulator(repopulate);
return () => {
unregisterPagePopulator(repopulate);
};
}, [dispatch]);
useEffect(() => {
if (previousIsDeleting && !isDeleting && !deleteError) {
setSelectState({ type: 'unselectAll', items });
dispatch(fetchImportListExclusions());
}
}, [
previousIsDeleting,
isDeleting,
deleteError,
items,
dispatch,
setSelectState,
]);
const [
isAddImportListExclusionModalOpen,
setAddImportListExclusionModalOpen,
setAddImportListExclusionModalClosed,
] = useModalOpenState(false);
const isFetchingForFirstTime = isFetching && !isPopulated;
return (
<FieldSet legend={translate('ImportListExclusions')}>
<PageSectionContent
errorMessage={translate('ImportListExclusionsLoadError')}
isFetching={isFetchingForFirstTime}
isPopulated={isPopulated}
error={error}
>
<Table
selectAll={true}
allSelected={allSelected}
allUnselected={allUnselected}
columns={COLUMNS}
canModifyColumns={false}
pageSize={pageSize}
sortKey={sortKey}
sortDirection={sortDirection}
onTableOptionChange={handleTableOptionChange}
onSelectAllChange={handleSelectAllChange}
onSortPress={handleSortPress}
>
<TableBody>
{items.map((item) => {
return (
<ImportListExclusionRow
key={item.id}
{...item}
isSelected={selectedState[item.id] || false}
onSelectedChange={handleSelectedChange}
/>
);
})}
<TableRow>
<TableRowCell colSpan={4}>
<SpinnerButton
kind={kinds.DANGER}
isSpinning={isDeleting}
isDisabled={!selectedIds.length}
onPress={handleDeleteSelectedPress}
>
{translate('Delete')}
</SpinnerButton>
</TableRowCell>
<TableRowCell>
<IconButton
name={icons.ADD}
onPress={setAddImportListExclusionModalOpen}
/>
</TableRowCell>
</TableRow>
</TableBody>
</Table>
<TablePager
page={page}
totalPages={totalPages}
totalRecords={totalRecords}
isFetching={isFetching}
onFirstPagePress={handleFirstPagePress}
onPreviousPagePress={handlePreviousPagePress}
onNextPagePress={handleNextPagePress}
onLastPagePress={handleLastPagePress}
onPageSelect={handlePageSelect}
/>
<EditImportListExclusionModal
isOpen={isAddImportListExclusionModalOpen}
onModalClose={setAddImportListExclusionModalClosed}
/>
<ConfirmModal
isOpen={isConfirmDeleteModalOpen}
kind={kinds.DANGER}
title={translate('DeleteSelected')}
message={translate('DeleteSelectedImportListExclusionsMessageText')}
confirmLabel={translate('DeleteSelected')}
onConfirm={handleDeleteSelectedConfirmed}
onCancel={handleConfirmDeleteModalClose}
/>
</PageSectionContent>
</FieldSet>
);
}
export default ImportListExclusions;

@ -1,59 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { deleteImportListExclusion, fetchImportListExclusions } from 'Store/Actions/settingsActions';
import ImportListExclusions from './ImportListExclusions';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.importListExclusions,
(importListExclusions) => {
return {
...importListExclusions
};
}
);
}
const mapDispatchToProps = {
fetchImportListExclusions,
deleteImportListExclusion
};
class ImportListExclusionsConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.fetchImportListExclusions();
}
//
// Listeners
onConfirmDeleteImportListExclusion = (id) => {
this.props.deleteImportListExclusion({ id });
};
//
// Render
render() {
return (
<ImportListExclusions
{...this.state}
{...this.props}
onConfirmDeleteImportListExclusion={this.onConfirmDeleteImportListExclusion}
/>
);
}
}
ImportListExclusionsConnector.propTypes = {
fetchImportListExclusions: PropTypes.func.isRequired,
deleteImportListExclusion: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(ImportListExclusionsConnector);

@ -7,7 +7,7 @@ import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector'; import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import ImportListExclusionsConnector from './ImportListExclusions/ImportListExclusionsConnector'; import ImportListExclusions from './ImportListExclusions/ImportListExclusions';
import ImportListsConnector from './ImportLists/ImportListsConnector'; import ImportListsConnector from './ImportLists/ImportListsConnector';
import ManageImportListsModal from './ImportLists/Manage/ManageImportListsModal'; import ManageImportListsModal from './ImportLists/Manage/ManageImportListsModal';
import ImportListOptions from './Options/ImportListOptions'; import ImportListOptions from './Options/ImportListOptions';
@ -103,7 +103,7 @@ class ImportListSettings extends Component {
onChildStateChange={this.onChildStateChange} onChildStateChange={this.onChildStateChange}
/> />
<ImportListExclusionsConnector /> <ImportListExclusions />
<ManageImportListsModal <ManageImportListsModal
isOpen={isManageImportListsOpen} isOpen={isManageImportListsOpen}

@ -1,9 +1,13 @@
import { createAction } from 'redux-actions'; import { createAction } from 'redux-actions';
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler'; import createBulkRemoveItemHandler from 'Store/Actions/Creators/createBulkRemoveItemHandler';
import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler'; import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler';
import createSaveProviderHandler from 'Store/Actions/Creators/createSaveProviderHandler'; import createSaveProviderHandler from 'Store/Actions/Creators/createSaveProviderHandler';
import createServerSideCollectionHandlers from 'Store/Actions/Creators/createServerSideCollectionHandlers';
import createClearReducer from 'Store/Actions/Creators/Reducers/createClearReducer';
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer'; import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
import { createThunk } from 'Store/thunks'; import createSetTableOptionReducer from 'Store/Actions/Creators/Reducers/createSetTableOptionReducer';
import { createThunk, handleThunks } from 'Store/thunks';
import serverSideCollectionHandlers from 'Utilities/serverSideCollectionHandlers';
// //
// Variables // Variables
@ -14,18 +18,28 @@ const section = 'settings.importListExclusions';
// Actions Types // Actions Types
export const FETCH_IMPORT_LIST_EXCLUSIONS = 'settings/importListExclusions/fetchImportListExclusions'; export const FETCH_IMPORT_LIST_EXCLUSIONS = 'settings/importListExclusions/fetchImportListExclusions';
export const GOTO_IMPORT_LIST_EXCLUSION_PAGE = 'settings/importListExclusions/gotoImportListExclusionPage';
export const SET_IMPORT_LIST_EXCLUSION_SORT = 'settings/importListExclusions/setImportListExclusionSort';
export const SAVE_IMPORT_LIST_EXCLUSION = 'settings/importListExclusions/saveImportListExclusion'; export const SAVE_IMPORT_LIST_EXCLUSION = 'settings/importListExclusions/saveImportListExclusion';
export const DELETE_IMPORT_LIST_EXCLUSION = 'settings/importListExclusions/deleteImportListExclusion'; export const DELETE_IMPORT_LIST_EXCLUSION = 'settings/importListExclusions/deleteImportListExclusion';
export const BULK_DELETE_IMPORT_LIST_EXCLUSIONS = 'settings/importListExclusions/bulkDeleteImportListExclusions';
export const CLEAR_IMPORT_LIST_EXCLUSIONS = 'settings/importListExclusions/clearImportListExclusions';
export const SET_IMPORT_LIST_EXCLUSION_TABLE_OPTION = 'settings/importListExclusions/setImportListExclusionTableOption';
export const SET_IMPORT_LIST_EXCLUSION_VALUE = 'settings/importListExclusions/setImportListExclusionValue'; export const SET_IMPORT_LIST_EXCLUSION_VALUE = 'settings/importListExclusions/setImportListExclusionValue';
// //
// Action Creators // Action Creators
export const fetchImportListExclusions = createThunk(FETCH_IMPORT_LIST_EXCLUSIONS); export const fetchImportListExclusions = createThunk(FETCH_IMPORT_LIST_EXCLUSIONS);
export const gotoImportListExclusionPage = createThunk(GOTO_IMPORT_LIST_EXCLUSION_PAGE);
export const setImportListExclusionSort = createThunk(SET_IMPORT_LIST_EXCLUSION_SORT);
export const saveImportListExclusion = createThunk(SAVE_IMPORT_LIST_EXCLUSION); export const saveImportListExclusion = createThunk(SAVE_IMPORT_LIST_EXCLUSION);
export const deleteImportListExclusion = createThunk(DELETE_IMPORT_LIST_EXCLUSION); export const deleteImportListExclusion = createThunk(DELETE_IMPORT_LIST_EXCLUSION);
export const bulkDeleteImportListExclusions = createThunk(BULK_DELETE_IMPORT_LIST_EXCLUSIONS);
export const clearImportListExclusions = createAction(CLEAR_IMPORT_LIST_EXCLUSIONS);
export const setImportListExclusionTableOption = createAction(SET_IMPORT_LIST_EXCLUSION_TABLE_OPTION);
export const setImportListExclusionValue = createAction(SET_IMPORT_LIST_EXCLUSION_VALUE, (payload) => { export const setImportListExclusionValue = createAction(SET_IMPORT_LIST_EXCLUSION_VALUE, (payload) => {
return { return {
section, section,
@ -45,27 +59,52 @@ export default {
isFetching: false, isFetching: false,
isPopulated: false, isPopulated: false,
error: null, error: null,
pageSize: 20,
items: [], items: [],
isSaving: false, isSaving: false,
saveError: null, saveError: null,
isDeleting: false,
deleteError: null,
pendingChanges: {} pendingChanges: {}
}, },
// //
// Action Handlers // Action Handlers
actionHandlers: { actionHandlers: handleThunks({
[FETCH_IMPORT_LIST_EXCLUSIONS]: createFetchHandler(section, '/exclusions'), ...createServerSideCollectionHandlers(
section,
'/exclusions/paged',
fetchImportListExclusions,
{
[serverSideCollectionHandlers.FETCH]: FETCH_IMPORT_LIST_EXCLUSIONS,
[serverSideCollectionHandlers.EXACT_PAGE]: GOTO_IMPORT_LIST_EXCLUSION_PAGE,
[serverSideCollectionHandlers.SORT]: SET_IMPORT_LIST_EXCLUSION_SORT
}
),
[SAVE_IMPORT_LIST_EXCLUSION]: createSaveProviderHandler(section, '/exclusions'), [SAVE_IMPORT_LIST_EXCLUSION]: createSaveProviderHandler(section, '/exclusions'),
[DELETE_IMPORT_LIST_EXCLUSION]: createRemoveItemHandler(section, '/exclusions') [DELETE_IMPORT_LIST_EXCLUSION]: createRemoveItemHandler(section, '/exclusions'),
}, [BULK_DELETE_IMPORT_LIST_EXCLUSIONS]: createBulkRemoveItemHandler(section, '/exclusions/bulk')
}),
// //
// Reducers // Reducers
reducers: { reducers: {
[SET_IMPORT_LIST_EXCLUSION_VALUE]: createSetSettingValueReducer(section) [SET_IMPORT_LIST_EXCLUSION_VALUE]: createSetSettingValueReducer(section),
[SET_IMPORT_LIST_EXCLUSION_TABLE_OPTION]: createSetTableOptionReducer(section),
[CLEAR_IMPORT_LIST_EXCLUSIONS]: createClearReducer(section, {
isFetching: false,
isPopulated: false,
error: null,
items: [],
isDeleting: false,
deleteError: null,
pendingChanges: {},
totalPages: 0,
totalRecords: 0
})
} }
}; };

@ -94,7 +94,8 @@ export const defaultState = {
}; };
export const persistState = [ export const persistState = [
'settings.advancedSettings' 'settings.advancedSettings',
'settings.importListExclusions.pageSize'
]; ];
// //

@ -1,45 +1,44 @@
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import AppSectionState, { import { AppSectionItemState } from 'App/State/AppSectionState';
AppSectionItemState,
} from 'App/State/AppSectionState';
import AppState from 'App/State/AppState'; import AppState from 'App/State/AppState';
import SettingsAppState from 'App/State/SettingsAppState';
import selectSettings from 'Store/Selectors/selectSettings'; import selectSettings from 'Store/Selectors/selectSettings';
import { PendingSection } from 'typings/pending'; import { PendingSection } from 'typings/pending';
type SettingNames = keyof Omit<AppState['settings'], 'advancedSettings'>; type SectionsWithItemNames = {
type GetSectionState<Name extends SettingNames> = AppState['settings'][Name]; [K in keyof SettingsAppState]: SettingsAppState[K] extends AppSectionItemState<unknown>
type GetSettingsSectionItemType<Name extends SettingNames> = ? K
GetSectionState<Name> extends AppSectionItemState<infer R>
? R
: GetSectionState<Name> extends AppSectionState<infer R>
? R
: never; : never;
}[keyof SettingsAppState];
type AppStateWithPending<Name extends SettingNames> = { type GetSectionState<Name extends SectionsWithItemNames> =
item?: GetSettingsSectionItemType<Name>; SettingsAppState[Name];
pendingChanges?: Partial<GetSettingsSectionItemType<Name>>; type GetSettingsSectionItemType<Name extends SectionsWithItemNames> =
saveError?: Error; GetSectionState<Name> extends AppSectionItemState<infer R> ? R : never;
} & GetSectionState<Name>;
function createSettingsSectionSelector<Name extends SettingNames>( function createSettingsSectionSelector<
section: Name Name extends SectionsWithItemNames,
) { T extends GetSettingsSectionItemType<Name>
>(section: Name) {
return createSelector( return createSelector(
(state: AppState) => state.settings[section], (state: AppState) => state.settings[section],
(sectionSettings) => { (sectionSettings) => {
const { item, pendingChanges, saveError, ...other } = const { item, pendingChanges, ...other } = sectionSettings;
sectionSettings as AppStateWithPending<Name>;
const { settings, ...rest } = selectSettings( const saveError =
item, 'saveError' in sectionSettings ? sectionSettings.saveError : undefined;
pendingChanges,
saveError const {
); settings,
pendingChanges: selectedPendingChanges,
...rest
} = selectSettings(item, pendingChanges, saveError);
return { return {
...other, ...other,
saveError, saveError,
settings: settings as PendingSection<GetSettingsSectionItemType<Name>>, settings: settings as PendingSection<T>,
pendingChanges: selectedPendingChanges as Partial<T>,
...rest, ...rest,
}; };
} }

@ -0,0 +1,7 @@
import ModelBase from 'App/ModelBase';
export default interface ImportListExclusion extends ModelBase {
tmdbId: number;
movieTitle: string;
movieYear: number;
}

@ -0,0 +1,6 @@
import Column from 'Components/Table/Column';
export interface TableOptionsChangePayload {
pageSize?: number;
columns: Column[];
}

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Movies.Events; using NzbDrone.Core.Movies.Events;
@ -11,6 +12,7 @@ namespace NzbDrone.Core.ImportLists.ImportExclusions
ImportListExclusion Add(ImportListExclusion importListExclusion); ImportListExclusion Add(ImportListExclusion importListExclusion);
List<ImportListExclusion> Add(List<ImportListExclusion> importListExclusions); List<ImportListExclusion> Add(List<ImportListExclusion> importListExclusions);
List<ImportListExclusion> All(); List<ImportListExclusion> All();
PagingSpec<ImportListExclusion> Paged(PagingSpec<ImportListExclusion> pagingSpec);
bool IsMovieExcluded(int tmdbId); bool IsMovieExcluded(int tmdbId);
void Delete(int id); void Delete(int id);
void Delete(List<int> ids); void Delete(List<int> ids);
@ -78,6 +80,11 @@ namespace NzbDrone.Core.ImportLists.ImportExclusions
return _repo.All().ToList(); return _repo.All().ToList();
} }
public PagingSpec<ImportListExclusion> Paged(PagingSpec<ImportListExclusion> pagingSpec)
{
return _repo.GetPaged(pagingSpec);
}
public List<int> AllExcludedTmdbIds() public List<int> AllExcludedTmdbIds()
{ {
return _repo.AllExcludedTmdbIds(); return _repo.AllExcludedTmdbIds();

@ -111,7 +111,7 @@
"TorrentDelay": "تأخير السيل", "TorrentDelay": "تأخير السيل",
"Tomorrow": "غدا", "Tomorrow": "غدا",
"Today": "اليوم", "Today": "اليوم",
"TmdbIdHelpText": "معرّف TMDb الخاص بالفيلم المراد استبعاده", "TmdbIdExcludeHelpText": "معرّف TMDb الخاص بالفيلم المراد استبعاده",
"TMDBId": "معرف TMDb", "TMDBId": "معرف TMDb",
"TMDb": "TMDb", "TMDb": "TMDb",
"Titles": "الألقاب", "Titles": "الألقاب",
@ -276,9 +276,9 @@
"MustNotContain": "يجب ألا يحتوي", "MustNotContain": "يجب ألا يحتوي",
"MustContain": "يجب أن يحتوي على", "MustContain": "يجب أن يحتوي على",
"MultiLanguage": "متعدد اللغات", "MultiLanguage": "متعدد اللغات",
"MovieYearHelpText": "عام الفيلم المطلوب استبعاده", "MovieYearToExcludeHelpText": "عام الفيلم المطلوب استبعاده",
"MovieYear": "سنة الفيلم", "MovieYear": "سنة الفيلم",
"MovieTitleHelpText": "عنوان الفيلم المراد استبعاده (يمكن أن يكون أي شيء ذي معنى)", "MovieTitleToExcludeHelpText": "عنوان الفيلم المراد استبعاده (يمكن أن يكون أي شيء ذي معنى)",
"MovieTitle": "عنوان الفيلم", "MovieTitle": "عنوان الفيلم",
"MoviesSelectedInterp": "تم تحديد {0} فيلم (أفلام)", "MoviesSelectedInterp": "تم تحديد {0} فيلم (أفلام)",
"Movies": "أفلام", "Movies": "أفلام",

@ -572,7 +572,7 @@
"Title": "Заглавие", "Title": "Заглавие",
"Titles": "Заглавия", "Titles": "Заглавия",
"TMDBId": "Идентификатор на TMDb", "TMDBId": "Идентификатор на TMDb",
"TmdbIdHelpText": "Идентификаторът на TMDb на филма за изключване", "TmdbIdExcludeHelpText": "Идентификаторът на TMDb на филма за изключване",
"Today": "Днес", "Today": "Днес",
"TorrentDelay": "Торент закъснение", "TorrentDelay": "Торент закъснение",
"TorrentDelayHelpText": "Забавете за минути, за да изчакате, преди да вземете порой", "TorrentDelayHelpText": "Забавете за минути, за да изчакате, преди да вземете порой",
@ -883,9 +883,9 @@
"Movies": "Филми", "Movies": "Филми",
"MoviesSelectedInterp": "{0} Избран / и филм / и", "MoviesSelectedInterp": "{0} Избран / и филм / и",
"MovieTitle": "Заглавие на филма", "MovieTitle": "Заглавие на филма",
"MovieTitleHelpText": "Заглавието на филма, който трябва да се изключи (може да бъде всичко смислено)", "MovieTitleToExcludeHelpText": "Заглавието на филма, който трябва да се изключи (може да бъде всичко смислено)",
"MovieYear": "Филмова година", "MovieYear": "Филмова година",
"MovieYearHelpText": "Годината на филма за изключване", "MovieYearToExcludeHelpText": "Годината на филма за изключване",
"MustContain": "Трябва да съдържа", "MustContain": "Трябва да съдържа",
"MustNotContain": "Не трябва да съдържа", "MustNotContain": "Не трябва да съдържа",
"NamingSettings": "Настройки за именуване", "NamingSettings": "Настройки за именуване",

@ -99,9 +99,9 @@
"MovieFolderFormat": "Format de carpeta de pel·lícules", "MovieFolderFormat": "Format de carpeta de pel·lícules",
"MovieIndex": "Índex de pel·lícules", "MovieIndex": "Índex de pel·lícules",
"MovieTitle": "Títol de la pel·lícula", "MovieTitle": "Títol de la pel·lícula",
"MovieTitleHelpText": "El títol de la pel·lícula a excloure (pot ser qualsevol cosa amb sentit)", "MovieTitleToExcludeHelpText": "El títol de la pel·lícula a excloure (pot ser qualsevol cosa amb sentit)",
"MovieYear": "Any de la pel·lícula", "MovieYear": "Any de la pel·lícula",
"MovieYearHelpText": "L'any de la pel·lícula a excloure", "MovieYearToExcludeHelpText": "L'any de la pel·lícula a excloure",
"NoAltTitle": "No hi ha títols alternatius.", "NoAltTitle": "No hi ha títols alternatius.",
"NoBackupsAreAvailable": "No hi ha còpies de seguretat disponibles", "NoBackupsAreAvailable": "No hi ha còpies de seguretat disponibles",
"NoChange": "Cap canvi", "NoChange": "Cap canvi",
@ -869,7 +869,7 @@
"Titles": "Títols", "Titles": "Títols",
"TMDb": "TMDb", "TMDb": "TMDb",
"TMDBId": "Identificador de TMDb", "TMDBId": "Identificador de TMDb",
"TmdbIdHelpText": "L'identificador de TMDb de la pel·lícula a excloure", "TmdbIdExcludeHelpText": "L'identificador de TMDb de la pel·lícula a excloure",
"TmdbVotes": "Vots de TMDb", "TmdbVotes": "Vots de TMDb",
"Today": "Avui", "Today": "Avui",
"Tomorrow": "Demà", "Tomorrow": "Demà",

@ -376,9 +376,9 @@
"Movies": "Filmy", "Movies": "Filmy",
"MoviesSelectedInterp": "{0} Vybrané filmy", "MoviesSelectedInterp": "{0} Vybrané filmy",
"MovieTitle": "Název filmu", "MovieTitle": "Název filmu",
"MovieTitleHelpText": "Název filmu, který se má vyloučit (může mít cokoli smysluplného)", "MovieTitleToExcludeHelpText": "Název filmu, který se má vyloučit (může mít cokoli smysluplného)",
"MovieYear": "Filmový rok", "MovieYear": "Filmový rok",
"MovieYearHelpText": "Rok filmu, který se má vyloučit", "MovieYearToExcludeHelpText": "Rok filmu, který se má vyloučit",
"MustContain": "Musí obsahovat", "MustContain": "Musí obsahovat",
"MustNotContain": "Nesmí obsahovat", "MustNotContain": "Nesmí obsahovat",
"NamingSettings": "Nastavení pojmenování", "NamingSettings": "Nastavení pojmenování",
@ -865,7 +865,7 @@
"Title": "Titul", "Title": "Titul",
"Titles": "Tituly", "Titles": "Tituly",
"TMDBId": "ID TMDb", "TMDBId": "ID TMDb",
"TmdbIdHelpText": "ID TMDb filmu, které se má vyloučit", "TmdbIdExcludeHelpText": "ID TMDb filmu, které se má vyloučit",
"Today": "Dnes", "Today": "Dnes",
"TorrentDelay": "Torrent Delay", "TorrentDelay": "Torrent Delay",
"TorrentDelayHelpText": "Zpoždění v minutách čekání před popadnutím torrentu", "TorrentDelayHelpText": "Zpoždění v minutách čekání před popadnutím torrentu",

@ -430,9 +430,9 @@
"MovieTitle": "Filmtitel", "MovieTitle": "Filmtitel",
"EditDelayProfile": "Rediger forsinkelsesprofil", "EditDelayProfile": "Rediger forsinkelsesprofil",
"Name": "Navn", "Name": "Navn",
"MovieTitleHelpText": "Titlen på den film, der skal ekskluderes (kan være noget meningsfuldt)", "MovieTitleToExcludeHelpText": "Titlen på den film, der skal ekskluderes (kan være noget meningsfuldt)",
"MovieYear": "Filmår", "MovieYear": "Filmår",
"MovieYearHelpText": "Året for filmen at udelukke", "MovieYearToExcludeHelpText": "Året for filmen at udelukke",
"MustContain": "Skal indeholde", "MustContain": "Skal indeholde",
"MustNotContain": "Må ikke indeholde", "MustNotContain": "Må ikke indeholde",
"NamingSettings": "Navngivningsindstillinger", "NamingSettings": "Navngivningsindstillinger",
@ -845,7 +845,7 @@
"Title": "Titel", "Title": "Titel",
"Titles": "Titler", "Titles": "Titler",
"TMDBId": "TMDb Id", "TMDBId": "TMDb Id",
"TmdbIdHelpText": "TMDb-id for filmen, der skal ekskluderes", "TmdbIdExcludeHelpText": "TMDb-id for filmen, der skal ekskluderes",
"Today": "I dag", "Today": "I dag",
"TorrentDelay": "Torrentforsinkelse", "TorrentDelay": "Torrentforsinkelse",
"TorrentDelayHelpText": "Forsink i minutter, før du tager fat i en torrent", "TorrentDelayHelpText": "Forsink i minutter, før du tager fat i en torrent",

@ -413,9 +413,9 @@
"MovieID": "Film ID", "MovieID": "Film ID",
"MovieInfoLanguageHelpTextWarning": "Seite muss neugeladen werden", "MovieInfoLanguageHelpTextWarning": "Seite muss neugeladen werden",
"MovieIsDownloading": "Film ist am herunterladen", "MovieIsDownloading": "Film ist am herunterladen",
"MovieTitleHelpText": "Der Titel des Filmes der augeschlossen werden soll", "MovieTitleToExcludeHelpText": "Der Titel des Filmes der augeschlossen werden soll",
"MovieYear": "Erscheinungsjahr", "MovieYear": "Erscheinungsjahr",
"MovieYearHelpText": "Das Erscheinungsjahr des Filmes der ausgeschlossen werden soll", "MovieYearToExcludeHelpText": "Das Erscheinungsjahr des Filmes der ausgeschlossen werden soll",
"MustContain": "Muss beinhalten", "MustContain": "Muss beinhalten",
"MustNotContain": "Darf nicht beinhalten", "MustNotContain": "Darf nicht beinhalten",
"NamingSettings": "Bennenungs Einstellungen", "NamingSettings": "Bennenungs Einstellungen",
@ -503,7 +503,7 @@
"Uptime": "Betriebszeit", "Uptime": "Betriebszeit",
"UrlBase": "URL-Basis", "UrlBase": "URL-Basis",
"TMDBId": "TMDb ID", "TMDBId": "TMDb ID",
"TmdbIdHelpText": "Die TMDb ID für den Ausschluss", "TmdbIdExcludeHelpText": "Die TMDb ID für den Ausschluss",
"TorrentDelay": "Torrent-Verzögerung", "TorrentDelay": "Torrent-Verzögerung",
"TorrentDelayHelpText": "Verzögerung in Minuten, bevor ein Torrent herunterladen wird", "TorrentDelayHelpText": "Verzögerung in Minuten, bevor ein Torrent herunterladen wird",
"Torrents": "Torrents", "Torrents": "Torrents",

@ -372,9 +372,9 @@
"MoviesSelectedInterp": "{0} Επιλεγμένες ταινίες", "MoviesSelectedInterp": "{0} Επιλεγμένες ταινίες",
"MovieTitle": "Τίτλος ταινίας", "MovieTitle": "Τίτλος ταινίας",
"Level": "Επίπεδο", "Level": "Επίπεδο",
"MovieTitleHelpText": "Ο τίτλος της ταινίας προς εξαίρεση (μπορεί να είναι οτιδήποτε νόημα)", "MovieTitleToExcludeHelpText": "Ο τίτλος της ταινίας προς εξαίρεση (μπορεί να είναι οτιδήποτε νόημα)",
"MovieYear": "Έτος ταινίας", "MovieYear": "Έτος ταινίας",
"MovieYearHelpText": "Το έτος της ταινίας που αποκλείεται", "MovieYearToExcludeHelpText": "Το έτος της ταινίας που αποκλείεται",
"MustContain": "Πρέπει να περιέχει", "MustContain": "Πρέπει να περιέχει",
"MustNotContain": "Δεν πρέπει να περιέχει", "MustNotContain": "Δεν πρέπει να περιέχει",
"NamingSettings": "Ρυθμίσεις ονομάτων", "NamingSettings": "Ρυθμίσεις ονομάτων",
@ -846,7 +846,7 @@
"Time": "χρόνος", "Time": "χρόνος",
"Title": "Τίτλος", "Title": "Τίτλος",
"Titles": "Τίτλοι", "Titles": "Τίτλοι",
"TmdbIdHelpText": "Το αναγνωριστικό TMDb της ταινίας για εξαίρεση", "TmdbIdExcludeHelpText": "Το αναγνωριστικό TMDb της ταινίας για εξαίρεση",
"Today": "Σήμερα", "Today": "Σήμερα",
"TorrentDelay": "Καθυστέρηση Torrent", "TorrentDelay": "Καθυστέρηση Torrent",
"TorrentDelayHelpText": "Καθυστέρηση σε λίγα λεπτά για να περιμένετε πριν πιάσετε ένα χείμαρρο", "TorrentDelayHelpText": "Καθυστέρηση σε λίγα λεπτά για να περιμένετε πριν πιάσετε ένα χείμαρρο",

@ -336,8 +336,10 @@
"DeleteRestrictionHelpText": "Are you sure you want to delete this restriction?", "DeleteRestrictionHelpText": "Are you sure you want to delete this restriction?",
"DeleteRootFolder": "Delete Root Folder", "DeleteRootFolder": "Delete Root Folder",
"DeleteRootFolderMessageText": "Are you sure you want to delete the root folder '{path}'?", "DeleteRootFolderMessageText": "Are you sure you want to delete the root folder '{path}'?",
"DeleteSelected": "Delete Selected",
"DeleteSelectedDownloadClients": "Delete Download Client(s)", "DeleteSelectedDownloadClients": "Delete Download Client(s)",
"DeleteSelectedDownloadClientsMessageText": "Are you sure you want to delete {count} selected download client(s)?", "DeleteSelectedDownloadClientsMessageText": "Are you sure you want to delete {count} selected download client(s)?",
"DeleteSelectedImportListExclusionsMessageText": "Are you sure you want to delete the selected import list exclusions?",
"DeleteSelectedImportLists": "Delete Import List(s)", "DeleteSelectedImportLists": "Delete Import List(s)",
"DeleteSelectedImportListsMessageText": "Are you sure you want to delete {count} selected import list(s)?", "DeleteSelectedImportListsMessageText": "Are you sure you want to delete {count} selected import list(s)?",
"DeleteSelectedIndexers": "Delete Indexer(s)", "DeleteSelectedIndexers": "Delete Indexer(s)",
@ -975,9 +977,9 @@
"MovieOnly": "Movie Only", "MovieOnly": "Movie Only",
"MovieSearchResultsLoadError": "Unable to load results for this movie search. Try again later", "MovieSearchResultsLoadError": "Unable to load results for this movie search. Try again later",
"MovieTitle": "Movie Title", "MovieTitle": "Movie Title",
"MovieTitleHelpText": "The title of the movie to exclude (can be anything meaningful)", "MovieTitleToExcludeHelpText": "The title of the movie to exclude (can be anything meaningful)",
"MovieYear": "Movie Year", "MovieYear": "Movie Year",
"MovieYearHelpText": "The year of the movie to exclude", "MovieYearToExcludeHelpText": "The year of the movie to exclude",
"Movies": "Movies", "Movies": "Movies",
"MoviesSelectedInterp": "{count} Movie(s) Selected", "MoviesSelectedInterp": "{count} Movie(s) Selected",
"MultiLanguage": "Multi-Language", "MultiLanguage": "Multi-Language",
@ -1674,7 +1676,7 @@
"Timeleft": "Time Left", "Timeleft": "Time Left",
"Title": "Title", "Title": "Title",
"Titles": "Titles", "Titles": "Titles",
"TmdbIdHelpText": "The TMDb Id of the movie to exclude", "TmdbIdExcludeHelpText": "The TMDb Id of the movie to exclude",
"TmdbRating": "TMDb Rating", "TmdbRating": "TMDb Rating",
"TmdbVotes": "TMDb Votes", "TmdbVotes": "TMDb Votes",
"Today": "Today", "Today": "Today",

@ -408,7 +408,7 @@
"Torrents": "Torrents", "Torrents": "Torrents",
"TorrentDelayHelpText": "Retraso en minutos a esperar antes de capturar un torrent", "TorrentDelayHelpText": "Retraso en minutos a esperar antes de capturar un torrent",
"TorrentDelay": "Retraso de torrent", "TorrentDelay": "Retraso de torrent",
"TmdbIdHelpText": "La Id de TMDb de la película a exluir", "TmdbIdExcludeHelpText": "La Id de TMDb de la película a exluir",
"TMDBId": "TMDb Id", "TMDBId": "TMDb Id",
"TestAllLists": "Probar todas las listas", "TestAllLists": "Probar todas las listas",
"TestAllIndexers": "Probar todos los indexadores", "TestAllIndexers": "Probar todos los indexadores",
@ -507,9 +507,9 @@
"NamingSettings": "Opciones de nombrado", "NamingSettings": "Opciones de nombrado",
"MustNotContain": "No debe contener", "MustNotContain": "No debe contener",
"MustContain": "Debe contener", "MustContain": "Debe contener",
"MovieYearHelpText": "Año de la película a excluir", "MovieYearToExcludeHelpText": "Año de la película a excluir",
"MovieYear": "Año de la Película", "MovieYear": "Año de la Película",
"MovieTitleHelpText": "Ttítulo de la película a excluir (puedes ser cualquier cosa)", "MovieTitleToExcludeHelpText": "Ttítulo de la película a excluir (puedes ser cualquier cosa)",
"MovieIsDownloading": "Le película está descargando", "MovieIsDownloading": "Le película está descargando",
"MovieInfoLanguageHelpTextWarning": "Recargar el Navegador", "MovieInfoLanguageHelpTextWarning": "Recargar el Navegador",
"MovieID": "ID de Película", "MovieID": "ID de Película",

@ -320,7 +320,7 @@
"RelativePath": "Suhteellinen sijainti", "RelativePath": "Suhteellinen sijainti",
"ReleaseRejected": "Vapautus hylätty", "ReleaseRejected": "Vapautus hylätty",
"MovieTitle": "Elokuvan nimi", "MovieTitle": "Elokuvan nimi",
"MovieTitleHelpText": "Poissuljettava elokuvan nimi (voi olla mikä tahansa mielekäs)", "MovieTitleToExcludeHelpText": "Poissuljettava elokuvan nimi (voi olla mikä tahansa mielekäs)",
"MovieYear": "Elokuvan vuosi", "MovieYear": "Elokuvan vuosi",
"TestAllClients": "Lataustyökalujen testaus", "TestAllClients": "Lataustyökalujen testaus",
"TestAllIndexers": "Tietolähteiden testaus", "TestAllIndexers": "Tietolähteiden testaus",
@ -328,7 +328,7 @@
"AddRemotePathMapping": "Lisää etäsijainnin kohdistus", "AddRemotePathMapping": "Lisää etäsijainnin kohdistus",
"Apply": "Käytä", "Apply": "Käytä",
"Analytics": "Analytiikka", "Analytics": "Analytiikka",
"MovieYearHelpText": "Elokuvan vuosi, joka suljetaan pois", "MovieYearToExcludeHelpText": "Elokuvan vuosi, joka suljetaan pois",
"Date": "Päiväys", "Date": "Päiväys",
"MustContain": "Täytyy sisältää", "MustContain": "Täytyy sisältää",
"MustNotContain": "Ei voi sisältää", "MustNotContain": "Ei voi sisältää",
@ -849,7 +849,7 @@
"Title": "Nimike", "Title": "Nimike",
"Titles": "Nimikkeet", "Titles": "Nimikkeet",
"TMDBId": "TMDB ID", "TMDBId": "TMDB ID",
"TmdbIdHelpText": "Ohitettavan elokuvan TMDB ID.", "TmdbIdExcludeHelpText": "Ohitettavan elokuvan TMDB ID.",
"Today": "Tänään", "Today": "Tänään",
"TorrentDelay": "Torrent-viive", "TorrentDelay": "Torrent-viive",
"TorrentDelayHelpText": "Minuuttiviive, joka odotetaan ennen julkaisun Torrent-kaappausta.", "TorrentDelayHelpText": "Minuuttiviive, joka odotetaan ennen julkaisun Torrent-kaappausta.",

@ -590,7 +590,7 @@
"UiLanguage": "Langue de l'IU", "UiLanguage": "Langue de l'IU",
"TotalFileSize": "Taille totale des fichiers", "TotalFileSize": "Taille totale des fichiers",
"Torrents": "Torrents", "Torrents": "Torrents",
"TmdbIdHelpText": "L'ID TMDb du film à exclure", "TmdbIdExcludeHelpText": "L'ID TMDb du film à exclure",
"TMDBId": "Identifiant TMDb", "TMDBId": "Identifiant TMDb",
"TestAllLists": "Tester toutes les listes", "TestAllLists": "Tester toutes les listes",
"TestAllIndexers": "Testez tous les indexeurs", "TestAllIndexers": "Testez tous les indexeurs",
@ -718,10 +718,10 @@
"NetCore": ".NET", "NetCore": ".NET",
"NamingSettings": "Paramètres de dénomination", "NamingSettings": "Paramètres de dénomination",
"MustNotContain": "Ne doit pas contenir", "MustNotContain": "Ne doit pas contenir",
"MovieYearHelpText": "L'année de film à exclure", "MovieYearToExcludeHelpText": "L'année de film à exclure",
"MustContain": "Doit contenir", "MustContain": "Doit contenir",
"MovieYear": "Année du film", "MovieYear": "Année du film",
"MovieTitleHelpText": "Le titre du film à exclure (peut être quelque chose de significatif)", "MovieTitleToExcludeHelpText": "Le titre du film à exclure (peut être quelque chose de significatif)",
"MovieIndexScrollTop": "Index des films : faire défiler vers le haut", "MovieIndexScrollTop": "Index des films : faire défiler vers le haut",
"MovieIndexScrollBottom": "Index des films : faire défiler vers le bas", "MovieIndexScrollBottom": "Index des films : faire défiler vers le bas",
"MovieDetailsPreviousMovie": "Détails du film : Film Précédent", "MovieDetailsPreviousMovie": "Détails du film : Film Précédent",

@ -9,7 +9,7 @@
"AvailabilityDelayHelpText": "משך הזמן לפני או אחרי התאריך הזמין לחיפוש הסרט", "AvailabilityDelayHelpText": "משך הזמן לפני או אחרי התאריך הזמין לחיפוש הסרט",
"BackupNow": "גיבוי עכשיו", "BackupNow": "גיבוי עכשיו",
"ChownGroupHelpText": "שם הקבוצה או ה- gid. השתמש ב- gid עבור מערכות קבצים מרוחקות.", "ChownGroupHelpText": "שם הקבוצה או ה- gid. השתמש ב- gid עבור מערכות קבצים מרוחקות.",
"MovieYearHelpText": "שנת הסרט לא הכללה", "MovieYearToExcludeHelpText": "שנת הסרט לא הכללה",
"ImportErrors": "ייבוא שגיאות", "ImportErrors": "ייבוא שגיאות",
"IndexersSettingsSummary": "אינדקסים ומגבלות שחרור", "IndexersSettingsSummary": "אינדקסים ומגבלות שחרור",
"ReplaceWithDash": "החלף ב- Dash", "ReplaceWithDash": "החלף ב- Dash",
@ -84,7 +84,7 @@
"WhitelistedSubtitleTags": "תגיות כתוביות ברשימת ההיתרים", "WhitelistedSubtitleTags": "תגיות כתוביות ברשימת ההיתרים",
"MissingMonitoredAndConsideredAvailable": "חסר (מנוטר)", "MissingMonitoredAndConsideredAvailable": "חסר (מנוטר)",
"ListTagsHelpText": "פריטי רשימת תגים יתווספו עם", "ListTagsHelpText": "פריטי רשימת תגים יתווספו עם",
"MovieTitleHelpText": "כותרת הסרט לאי הכללה (יכולה להיות כל דבר בעל משמעות)", "MovieTitleToExcludeHelpText": "כותרת הסרט לאי הכללה (יכולה להיות כל דבר בעל משמעות)",
"MovieYear": "שנת הסרט", "MovieYear": "שנת הסרט",
"DownloadPropersAndRepacksHelpTextCustomFormat": "השתמש ב'אל תעדיף 'כדי למיין לפי ציון פורמט מותאם אישית על פני Propers / Repacks", "DownloadPropersAndRepacksHelpTextCustomFormat": "השתמש ב'אל תעדיף 'כדי למיין לפי ציון פורמט מותאם אישית על פני Propers / Repacks",
"AddListExclusionMovieHelpText": "מנע מלהוסיף סרט לרדאר על ידי רשימות", "AddListExclusionMovieHelpText": "מנע מלהוסיף סרט לרדאר על ידי רשימות",
@ -850,7 +850,7 @@
"Title": "כותרת", "Title": "כותרת",
"Titles": "כותרות", "Titles": "כותרות",
"TMDBId": "מזהה TMDb", "TMDBId": "מזהה TMDb",
"TmdbIdHelpText": "מזהה ה- TMDb של הסרט לא לכלול", "TmdbIdExcludeHelpText": "מזהה ה- TMDb של הסרט לא לכלול",
"Today": "היום", "Today": "היום",
"TorrentDelay": "עיכוב סיקור", "TorrentDelay": "עיכוב סיקור",
"TorrentDelayHelpText": "התעכב תוך דקות להמתין לפני שתופס סיקור", "TorrentDelayHelpText": "התעכב תוך דקות להמתין לפני שתופס סיקור",

@ -516,9 +516,9 @@
"Movies": "चलचित्र", "Movies": "चलचित्र",
"MoviesSelectedInterp": "{0} मूवी (s) चयनित", "MoviesSelectedInterp": "{0} मूवी (s) चयनित",
"MovieTitle": "चलचित्र शीर्षक", "MovieTitle": "चलचित्र शीर्षक",
"MovieTitleHelpText": "बहिष्कृत करने के लिए फिल्म का शीर्षक (सार्थक कुछ भी हो सकता है)", "MovieTitleToExcludeHelpText": "बहिष्कृत करने के लिए फिल्म का शीर्षक (सार्थक कुछ भी हो सकता है)",
"MovieYear": "मूवी वर्ष", "MovieYear": "मूवी वर्ष",
"MovieYearHelpText": "फिल्म को बाहर करने का वर्ष", "MovieYearToExcludeHelpText": "फिल्म को बाहर करने का वर्ष",
"MustContain": "शामिल होना चाहिए", "MustContain": "शामिल होना चाहिए",
"MustNotContain": "कंटेनर नहीं होना चाहिए", "MustNotContain": "कंटेनर नहीं होना चाहिए",
"NamingSettings": "नामकरण सेटिंग्स", "NamingSettings": "नामकरण सेटिंग्स",
@ -888,7 +888,7 @@
"ThisCannotBeCancelled": "यह एक बार रद्द नहीं किया जा सकता है जब तक कि रेडर को फिर से शुरू किए बिना।", "ThisCannotBeCancelled": "यह एक बार रद्द नहीं किया जा सकता है जब तक कि रेडर को फिर से शुरू किए बिना।",
"Time": "समय", "Time": "समय",
"TMDBId": "TMDb Id", "TMDBId": "TMDb Id",
"TmdbIdHelpText": "बाहर करने के लिए फिल्म की TMDb Id", "TmdbIdExcludeHelpText": "बाहर करने के लिए फिल्म की TMDb Id",
"Today": "आज", "Today": "आज",
"TorrentDelayTime": "धार विलंब: {0}", "TorrentDelayTime": "धार विलंब: {0}",
"Torrents": "टोरेंट", "Torrents": "टोरेंट",

@ -482,9 +482,9 @@
"Name": "Név", "Name": "Név",
"MustNotContain": "Nem tartalmazhat", "MustNotContain": "Nem tartalmazhat",
"MustContain": "Tartalmaznia kell", "MustContain": "Tartalmaznia kell",
"MovieYearHelpText": "A kizárandó film(ek) éve", "MovieYearToExcludeHelpText": "A kizárandó film(ek) éve",
"MovieYear": "Kiadási év", "MovieYear": "Kiadási év",
"MovieTitleHelpText": "A kizárandó film címe (bármi értelmes lehet)", "MovieTitleToExcludeHelpText": "A kizárandó film címe (bármi értelmes lehet)",
"MovieTitle": "Filmcím", "MovieTitle": "Filmcím",
"MoviesSelectedInterp": "{0} Kiválasztott film(ek)", "MoviesSelectedInterp": "{0} Kiválasztott film(ek)",
"Movies": "Filmek", "Movies": "Filmek",
@ -619,7 +619,7 @@
"Torrents": "Torrentek", "Torrents": "Torrentek",
"TorrentDelayHelpText": "Percek késése, hogy várjon, mielőtt megragad egy torrentet", "TorrentDelayHelpText": "Percek késése, hogy várjon, mielőtt megragad egy torrentet",
"TorrentDelay": "Torrent Késleltetés", "TorrentDelay": "Torrent Késleltetés",
"TmdbIdHelpText": "A kizárandó film TMDb azonosítója", "TmdbIdExcludeHelpText": "A kizárandó film TMDb azonosítója",
"TMDBId": "TMDb azonosító", "TMDBId": "TMDb azonosító",
"Titles": "Címek", "Titles": "Címek",
"Title": "Cím", "Title": "Cím",

@ -342,7 +342,7 @@
"Calendar": "Dagatal", "Calendar": "Dagatal",
"MoviesSelectedInterp": "{0} Kvikmynd (ir) valdar", "MoviesSelectedInterp": "{0} Kvikmynd (ir) valdar",
"MovieTitle": "Kvikmyndatitill", "MovieTitle": "Kvikmyndatitill",
"MovieTitleHelpText": "Titill myndarinnar til að útiloka (getur verið hvað sem er þýðingarmikill)", "MovieTitleToExcludeHelpText": "Titill myndarinnar til að útiloka (getur verið hvað sem er þýðingarmikill)",
"ClientPriority": "Forgangur viðskiptavinar", "ClientPriority": "Forgangur viðskiptavinar",
"DeleteCustomFormat": "Eyða sérsniðnu sniði", "DeleteCustomFormat": "Eyða sérsniðnu sniði",
"DockerUpdater": "uppfærðu bryggjugáminn til að fá uppfærsluna", "DockerUpdater": "uppfærðu bryggjugáminn til að fá uppfærsluna",
@ -358,7 +358,7 @@
"InvalidFormat": "Ógilt snið", "InvalidFormat": "Ógilt snið",
"Medium": "Miðlungs", "Medium": "Miðlungs",
"Minutes": "Fundargerð", "Minutes": "Fundargerð",
"MovieYearHelpText": "Ár kvikmyndarinnar til að útiloka", "MovieYearToExcludeHelpText": "Ár kvikmyndarinnar til að útiloka",
"Profiles": "Snið", "Profiles": "Snið",
"Reason": "Ástæða", "Reason": "Ástæða",
"Released": "Sleppt", "Released": "Sleppt",
@ -859,7 +859,7 @@
"Title": "Titill", "Title": "Titill",
"Titles": "Titlar", "Titles": "Titlar",
"TMDBId": "TMDb kt", "TMDBId": "TMDb kt",
"TmdbIdHelpText": "TMDb auðkenni myndarinnar til að útiloka", "TmdbIdExcludeHelpText": "TMDb auðkenni myndarinnar til að útiloka",
"Today": "Í dag", "Today": "Í dag",
"TorrentDelay": "Torrent Delay", "TorrentDelay": "Torrent Delay",
"TorrentDelayHelpText": "Seinkaðu í nokkrar mínútur til að bíða áður en þú grípur strauminn", "TorrentDelayHelpText": "Seinkaðu í nokkrar mínútur til að bíða áður en þú grípur strauminn",

@ -466,9 +466,9 @@
"NamingSettings": "Impostazioni di denominazione", "NamingSettings": "Impostazioni di denominazione",
"MustNotContain": "Non Deve Contenere", "MustNotContain": "Non Deve Contenere",
"MustContain": "Deve Contenere", "MustContain": "Deve Contenere",
"MovieYearHelpText": "Anno del film da escludere", "MovieYearToExcludeHelpText": "Anno del film da escludere",
"MovieYear": "Anno del film", "MovieYear": "Anno del film",
"MovieTitleHelpText": "Titolo del Film da escludere (può essere qualunque cosa significativa)", "MovieTitleToExcludeHelpText": "Titolo del Film da escludere (può essere qualunque cosa significativa)",
"MoviesSelectedInterp": "{count} Film selezionato(i)", "MoviesSelectedInterp": "{count} Film selezionato(i)",
"MovieIsUnmonitored": "Il film è non monitorato", "MovieIsUnmonitored": "Il film è non monitorato",
"MovieIsOnImportExclusionList": "Il Film è nella lista di esclusione dell'importazione", "MovieIsOnImportExclusionList": "Il Film è nella lista di esclusione dell'importazione",
@ -654,7 +654,7 @@
"Torrents": "Torrents", "Torrents": "Torrents",
"TorrentDelayHelpText": "Ritardo in minuti da aspettare prima di prendere un torrent", "TorrentDelayHelpText": "Ritardo in minuti da aspettare prima di prendere un torrent",
"TorrentDelay": "Ritardo del torrent", "TorrentDelay": "Ritardo del torrent",
"TmdbIdHelpText": "Id di TMDb del film da escludere", "TmdbIdExcludeHelpText": "Id di TMDb del film da escludere",
"TMDBId": "ID di TMDb", "TMDBId": "ID di TMDb",
"TimeFormat": "Formato Orario", "TimeFormat": "Formato Orario",
"TestAllLists": "Testa tutte le liste", "TestAllLists": "Testa tutte le liste",

@ -89,7 +89,7 @@
"AuthenticationMethodHelpText": "{appName}にアクセスするにはユーザー名とパスワードが必要です", "AuthenticationMethodHelpText": "{appName}にアクセスするにはユーザー名とパスワードが必要です",
"Component": "成分", "Component": "成分",
"Proxy": "プロキシ", "Proxy": "プロキシ",
"MovieTitleHelpText": "除外する映画のタイトル(意味のあるものであれば何でもかまいません)", "MovieTitleToExcludeHelpText": "除外する映画のタイトル(意味のあるものであれば何でもかまいません)",
"TestAllIndexers": "すべてのインデクサーをテストする", "TestAllIndexers": "すべてのインデクサーをテストする",
"AnalyseVideoFiles": "ビデオファイルを分析する", "AnalyseVideoFiles": "ビデオファイルを分析する",
"AppDataDirectory": "AppDataディレクトリ", "AppDataDirectory": "AppDataディレクトリ",
@ -333,7 +333,7 @@
"Actions": "行動", "Actions": "行動",
"AddMovies": "映画を追加する", "AddMovies": "映画を追加する",
"IndexersSettingsSummary": "インデクサーとリリース制限", "IndexersSettingsSummary": "インデクサーとリリース制限",
"MovieYearHelpText": "除外する映画の年", "MovieYearToExcludeHelpText": "除外する映画の年",
"Age": "年齢", "Age": "年齢",
"NetCore": ".NET Core", "NetCore": ".NET Core",
"NoBackupsAreAvailable": "バックアップは利用できません", "NoBackupsAreAvailable": "バックアップは利用できません",
@ -847,7 +847,7 @@
"Title": "題名", "Title": "題名",
"Titles": "タイトル", "Titles": "タイトル",
"TMDBId": "TMDbID", "TMDBId": "TMDbID",
"TmdbIdHelpText": "除外する映画のTMDbID", "TmdbIdExcludeHelpText": "除外する映画のTMDbID",
"Today": "今日", "Today": "今日",
"TorrentDelay": "トレント遅延", "TorrentDelay": "トレント遅延",
"Trace": "痕跡", "Trace": "痕跡",

@ -328,7 +328,7 @@
"Movies": "영화", "Movies": "영화",
"MoviesSelectedInterp": "선택한 영화 {0} 개", "MoviesSelectedInterp": "선택한 영화 {0} 개",
"MovieTitle": "영화 제목", "MovieTitle": "영화 제목",
"MovieTitleHelpText": "제외 할 영화 제목 (의미있는 것은 무엇이든 가능)", "MovieTitleToExcludeHelpText": "제외 할 영화 제목 (의미있는 것은 무엇이든 가능)",
"Actions": "동작", "Actions": "동작",
"Added": "추가됨", "Added": "추가됨",
"AddNew": "새로 추가하기", "AddNew": "새로 추가하기",
@ -337,7 +337,7 @@
"Agenda": "일정", "Agenda": "일정",
"Failed": "실패한", "Failed": "실패한",
"MovieYear": "영화 연도", "MovieYear": "영화 연도",
"MovieYearHelpText": "제외 할 영화 연도", "MovieYearToExcludeHelpText": "제외 할 영화 연도",
"MustContain": "포함해야 함", "MustContain": "포함해야 함",
"MustNotContain": "포함해서는 안 됨", "MustNotContain": "포함해서는 안 됨",
"NamingSettings": "이름 지정 설정", "NamingSettings": "이름 지정 설정",
@ -854,7 +854,7 @@
"Title": "제목", "Title": "제목",
"Titles": "제목", "Titles": "제목",
"TMDBId": "TMDb ID", "TMDBId": "TMDb ID",
"TmdbIdHelpText": "제외 할 영화의 TMDb ID", "TmdbIdExcludeHelpText": "제외 할 영화의 TMDb ID",
"Today": "오늘", "Today": "오늘",
"TorrentDelay": "급류 지연", "TorrentDelay": "급류 지연",
"TorrentDelayHelpText": "급류를 잡기 전에 대기하는 데 몇 분이 걸립니다.", "TorrentDelayHelpText": "급류를 잡기 전에 대기하는 데 몇 분이 걸립니다.",

@ -483,8 +483,8 @@
"MinimumCustomFormatScoreHelpText": "Minimum eigen formaat score toegelaten om te downloaden", "MinimumCustomFormatScoreHelpText": "Minimum eigen formaat score toegelaten om te downloaden",
"MinimumFreeSpaceHelpText": "Voorkom importeren indien de resulterende schijfruimte minder is dan deze hoeveelheid", "MinimumFreeSpaceHelpText": "Voorkom importeren indien de resulterende schijfruimte minder is dan deze hoeveelheid",
"MovieIsOnImportExclusionList": "Film staat op de uitzonderingenlijst voor importeren", "MovieIsOnImportExclusionList": "Film staat op de uitzonderingenlijst voor importeren",
"MovieTitleHelpText": "De titel van de uit te sluiten film (kan van alles zijn)", "MovieTitleToExcludeHelpText": "De titel van de uit te sluiten film (kan van alles zijn)",
"MovieYearHelpText": "Het jaar van de uit te sluiten film", "MovieYearToExcludeHelpText": "Het jaar van de uit te sluiten film",
"NotificationTriggers": "Melding Reactiestarters", "NotificationTriggers": "Melding Reactiestarters",
"OpenBrowserOnStart": "Open de browser bij het starten", "OpenBrowserOnStart": "Open de browser bij het starten",
"NoLimitForAnyRuntime": "Geen limiet voor eender welke speelduur", "NoLimitForAnyRuntime": "Geen limiet voor eender welke speelduur",
@ -554,7 +554,7 @@
"TestAllClients": "Test Alle Downloaders", "TestAllClients": "Test Alle Downloaders",
"TestAllLists": "Test Alle Lijsten", "TestAllLists": "Test Alle Lijsten",
"TMDBId": "TMDb Id", "TMDBId": "TMDb Id",
"TmdbIdHelpText": "De TMDb Id van de uit te sluiten film", "TmdbIdExcludeHelpText": "De TMDb Id van de uit te sluiten film",
"TorrentDelay": "Torrent Vertraging", "TorrentDelay": "Torrent Vertraging",
"Torrents": "Torrents", "Torrents": "Torrents",
"TotalFileSize": "Totale Bestandsgrootte", "TotalFileSize": "Totale Bestandsgrootte",

@ -336,9 +336,9 @@
"MovieIsUnmonitored": "Film jest niemonitorowany", "MovieIsUnmonitored": "Film jest niemonitorowany",
"MoviesSelectedInterp": "Wybrane filmy: {0}", "MoviesSelectedInterp": "Wybrane filmy: {0}",
"MovieTitle": "Tytuł filmu", "MovieTitle": "Tytuł filmu",
"MovieTitleHelpText": "Tytuł filmu do wykluczenia (może być dowolny znaczący)", "MovieTitleToExcludeHelpText": "Tytuł filmu do wykluczenia (może być dowolny znaczący)",
"MovieYear": "Rok filmowy", "MovieYear": "Rok filmowy",
"MovieYearHelpText": "Rok filmu do wykluczenia", "MovieYearToExcludeHelpText": "Rok filmu do wykluczenia",
"MustContain": "Musi zawierać", "MustContain": "Musi zawierać",
"MustNotContain": "Nie może zawierać", "MustNotContain": "Nie może zawierać",
"NamingSettings": "Ustawienia nazewnictwa", "NamingSettings": "Ustawienia nazewnictwa",
@ -859,7 +859,7 @@
"Title": "Tytuł", "Title": "Tytuł",
"Titles": "Tytuły", "Titles": "Tytuły",
"TMDBId": "Identyfikator TMDb", "TMDBId": "Identyfikator TMDb",
"TmdbIdHelpText": "Identyfikator TMDb filmu do wykluczenia", "TmdbIdExcludeHelpText": "Identyfikator TMDb filmu do wykluczenia",
"Today": "Dzisiaj", "Today": "Dzisiaj",
"TorrentDelay": "Opóźnienie Torrenta", "TorrentDelay": "Opóźnienie Torrenta",
"TorrentDelayHelpText": "Opóźnienie w ciągu kilku minut, aby poczekać przed złapaniem torrenta", "TorrentDelayHelpText": "Opóźnienie w ciągu kilku minut, aby poczekać przed złapaniem torrenta",

@ -335,9 +335,9 @@
"NamingSettings": "Definições de nomenclatura", "NamingSettings": "Definições de nomenclatura",
"MustNotContain": "Não deve conter", "MustNotContain": "Não deve conter",
"MustContain": "Deve conter", "MustContain": "Deve conter",
"MovieYearHelpText": "Ano do filme a eliminar", "MovieYearToExcludeHelpText": "Ano do filme a eliminar",
"MovieYear": "Ano do filme", "MovieYear": "Ano do filme",
"MovieTitleHelpText": "Título do filme a eliminar (pode ser qualquer palavra)", "MovieTitleToExcludeHelpText": "Título do filme a eliminar (pode ser qualquer palavra)",
"MovieIsDownloading": "Transferindo filme", "MovieIsDownloading": "Transferindo filme",
"MovieInfoLanguageHelpTextWarning": "É preciso reiniciar o browser", "MovieInfoLanguageHelpTextWarning": "É preciso reiniciar o browser",
"MovieID": "ID do filme", "MovieID": "ID do filme",
@ -569,7 +569,7 @@
"ExtraFileExtensionsHelpTextsExamples": "Exemplos: \".sub, .nfo\" ou \"sub,nfo\"", "ExtraFileExtensionsHelpTextsExamples": "Exemplos: \".sub, .nfo\" ou \"sub,nfo\"",
"ExtraFileExtensionsHelpText": "Lista separada por vírgulas de ficheiros adicionais a importar (.nfo será importado como .nfo-orig)", "ExtraFileExtensionsHelpText": "Lista separada por vírgulas de ficheiros adicionais a importar (.nfo será importado como .nfo-orig)",
"ExistingTag": "Etiqueta existente", "ExistingTag": "Etiqueta existente",
"TmdbIdHelpText": "ID do TMDb do filme a eliminar", "TmdbIdExcludeHelpText": "ID do TMDb do filme a eliminar",
"MovieExcludedFromAutomaticAdd": "Filme eliminado da adição automática", "MovieExcludedFromAutomaticAdd": "Filme eliminado da adição automática",
"MovieAlreadyExcluded": "Filme já eliminado", "MovieAlreadyExcluded": "Filme já eliminado",
"ExcludeMovie": "Eliminar filme", "ExcludeMovie": "Eliminar filme",

@ -462,9 +462,9 @@
"About": "Sobre", "About": "Sobre",
"Analytics": "Análises", "Analytics": "Análises",
"Month": "Mês", "Month": "Mês",
"MovieYearHelpText": "O ano do filme a excluir", "MovieYearToExcludeHelpText": "O ano do filme a excluir",
"MovieYear": "Ano do filme", "MovieYear": "Ano do filme",
"MovieTitleHelpText": "O título do filme a excluir (pode ser qualquer coisa significativa)", "MovieTitleToExcludeHelpText": "O título do filme a excluir (pode ser qualquer coisa significativa)",
"MovieTitle": "Título do filme", "MovieTitle": "Título do filme",
"MoviesSelectedInterp": "{count} Filme(s) selecionado(s)", "MoviesSelectedInterp": "{count} Filme(s) selecionado(s)",
"Movies": "Filmes", "Movies": "Filmes",
@ -764,7 +764,7 @@
"Ui": "IU", "Ui": "IU",
"Type": "Tipo", "Type": "Tipo",
"Torrents": "Torrents", "Torrents": "Torrents",
"TmdbIdHelpText": "A ID do TMDb do filme a excluir", "TmdbIdExcludeHelpText": "A ID do TMDb do filme a excluir",
"TMDBId": "ID do TMDb", "TMDBId": "ID do TMDb",
"TMDb": "TMDb", "TMDb": "TMDb",
"SslCertPathHelpText": "Caminho para o arquivo pfx", "SslCertPathHelpText": "Caminho para o arquivo pfx",

@ -519,12 +519,12 @@
"BranchUpdate": "Sucursală de utilizat pentru actualizarea {appName}", "BranchUpdate": "Sucursală de utilizat pentru actualizarea {appName}",
"BranchUpdateMechanism": "Ramură utilizată de mecanismul extern de actualizare", "BranchUpdateMechanism": "Ramură utilizată de mecanismul extern de actualizare",
"BypassProxyForLocalAddresses": "Nu folosiți Proxy pentru adrese locale", "BypassProxyForLocalAddresses": "Nu folosiți Proxy pentru adrese locale",
"MovieTitleHelpText": "Titlul filmului de exclus (poate avea orice sens)", "MovieTitleToExcludeHelpText": "Titlul filmului de exclus (poate avea orice sens)",
"Local": "Local", "Local": "Local",
"MovieYear": "Anul filmului", "MovieYear": "Anul filmului",
"RecyclingBinCleanup": "Curățarea coșului de reciclare", "RecyclingBinCleanup": "Curățarea coșului de reciclare",
"RefreshMovie": "Reîmprospătați filmul", "RefreshMovie": "Reîmprospătați filmul",
"MovieYearHelpText": "Anul filmului de exclus", "MovieYearToExcludeHelpText": "Anul filmului de exclus",
"MustContain": "Trebuie sa contina", "MustContain": "Trebuie sa contina",
"MustNotContain": "Nu trebuie să conțină", "MustNotContain": "Nu trebuie să conțină",
"NamingSettings": "Setări de denumire", "NamingSettings": "Setări de denumire",
@ -877,7 +877,7 @@
"TagIsNotUsedAndCanBeDeleted": "Eticheta nu este utilizată și poate fi ștearsă", "TagIsNotUsedAndCanBeDeleted": "Eticheta nu este utilizată și poate fi ștearsă",
"ICalTagsMoviesHelpText": "Se aplică filmelor cu cel puțin o etichetă potrivită", "ICalTagsMoviesHelpText": "Se aplică filmelor cu cel puțin o etichetă potrivită",
"TMDBId": "Cod TMDb", "TMDBId": "Cod TMDb",
"TmdbIdHelpText": "Codul TMDb al filmului de exclus", "TmdbIdExcludeHelpText": "Codul TMDb al filmului de exclus",
"Today": "Astăzi", "Today": "Astăzi",
"TorrentDelay": "Întârziere Torrent", "TorrentDelay": "Întârziere Torrent",
"TorrentDelayTime": "Întârziere Torrent: {0}", "TorrentDelayTime": "Întârziere Torrent: {0}",

@ -490,9 +490,9 @@
"MustNotContain": "Не должен содержать", "MustNotContain": "Не должен содержать",
"MustContain": "Должен содержать", "MustContain": "Должен содержать",
"MultiLanguage": "Многоязычный", "MultiLanguage": "Многоязычный",
"MovieYearHelpText": "Год фильма для исключений", "MovieYearToExcludeHelpText": "Год фильма для исключений",
"MovieYear": "Год фильма", "MovieYear": "Год фильма",
"MovieTitleHelpText": "Название фильма для исключения (может быть любым)", "MovieTitleToExcludeHelpText": "Название фильма для исключения (может быть любым)",
"MovieTitle": "Название фильма", "MovieTitle": "Название фильма",
"MoviesSelectedInterp": "{count} фильм(ов) выбрано", "MoviesSelectedInterp": "{count} фильм(ов) выбрано",
"Movies": "Фильмы", "Movies": "Фильмы",
@ -771,7 +771,7 @@
"TorrentDelay": "Задержка торрента", "TorrentDelay": "Задержка торрента",
"Tomorrow": "Завтра", "Tomorrow": "Завтра",
"Today": "Сегодня", "Today": "Сегодня",
"TmdbIdHelpText": "Идентификатор TMDb фильма, который нужно исключить", "TmdbIdExcludeHelpText": "Идентификатор TMDb фильма, который нужно исключить",
"TMDBId": "TMDb идентификатор", "TMDBId": "TMDb идентификатор",
"TMDb": "TMDb", "TMDb": "TMDb",
"SearchAll": "Искать все", "SearchAll": "Искать все",

@ -709,9 +709,9 @@
"DatabaseMigration": "DB Migration", "DatabaseMigration": "DB Migration",
"ImportExtraFiles": "Importera extra filer", "ImportExtraFiles": "Importera extra filer",
"ImportExtraFilesMovieHelpText": "Importera matchande extrafiler (undertexter, nfo, etc) efter import av en filmfil", "ImportExtraFilesMovieHelpText": "Importera matchande extrafiler (undertexter, nfo, etc) efter import av en filmfil",
"MovieTitleHelpText": "Filmens titel att utesluta (kan vara något meningsfullt)", "MovieTitleToExcludeHelpText": "Filmens titel att utesluta (kan vara något meningsfullt)",
"MinimumCustomFormatScoreHelpText": "Lägsta möjliga anpassade formatpoäng tillåtet att ladda ner", "MinimumCustomFormatScoreHelpText": "Lägsta möjliga anpassade formatpoäng tillåtet att ladda ner",
"MovieYearHelpText": "Året för filmen att utesluta", "MovieYearToExcludeHelpText": "Året för filmen att utesluta",
"ProxyType": "Proxy-typ", "ProxyType": "Proxy-typ",
"NamingSettings": "Namninställningar", "NamingSettings": "Namninställningar",
"LastDuration": "lastDuration", "LastDuration": "lastDuration",
@ -899,7 +899,7 @@
"TheLogLevelDefault": "Loggnivån är som standard 'Info' och kan ändras i", "TheLogLevelDefault": "Loggnivån är som standard 'Info' och kan ändras i",
"ThisCannotBeCancelled": "Detta kan inte avbrytas en gång startat utan att {appName} startas om.", "ThisCannotBeCancelled": "Detta kan inte avbrytas en gång startat utan att {appName} startas om.",
"TMDBId": "TMDb-id", "TMDBId": "TMDb-id",
"TmdbIdHelpText": "TMDb-id för filmen att utesluta", "TmdbIdExcludeHelpText": "TMDb-id för filmen att utesluta",
"TorrentDelay": "Torrentfördröjning", "TorrentDelay": "Torrentfördröjning",
"TorrentDelayHelpText": "Fördröja på några minuter för att vänta innan du tar en torrent", "TorrentDelayHelpText": "Fördröja på några minuter för att vänta innan du tar en torrent",
"TorrentDelayTime": "Torrentfördröjning: {0}", "TorrentDelayTime": "Torrentfördröjning: {0}",

@ -169,7 +169,7 @@
"TagsSettingsSummary": "ดูแท็กทั้งหมดและวิธีการใช้งาน แท็กที่ไม่ได้ใช้สามารถลบออกได้", "TagsSettingsSummary": "ดูแท็กทั้งหมดและวิธีการใช้งาน แท็กที่ไม่ได้ใช้สามารถลบออกได้",
"TheLogLevelDefault": "ระดับการบันทึกมีค่าเริ่มต้นเป็น \"ข้อมูล\" และสามารถเปลี่ยนแปลงได้", "TheLogLevelDefault": "ระดับการบันทึกมีค่าเริ่มต้นเป็น \"ข้อมูล\" และสามารถเปลี่ยนแปลงได้",
"Titles": "ชื่อเรื่อง", "Titles": "ชื่อเรื่อง",
"TmdbIdHelpText": "TMDb Id ของภาพยนตร์ที่จะยกเว้น", "TmdbIdExcludeHelpText": "TMDb Id ของภาพยนตร์ที่จะยกเว้น",
"TorrentsDisabled": "Torrents ถูกปิดใช้งาน", "TorrentsDisabled": "Torrents ถูกปิดใช้งาน",
"UiLanguageHelpText": "ภาษาที่ {appName} จะใช้สำหรับ UI", "UiLanguageHelpText": "ภาษาที่ {appName} จะใช้สำหรับ UI",
"UiSettingsSummary": "ตัวเลือกปฏิทินวันที่และสีบกพร่อง", "UiSettingsSummary": "ตัวเลือกปฏิทินวันที่และสีบกพร่อง",
@ -442,9 +442,9 @@
"Movies": "ภาพยนตร์", "Movies": "ภาพยนตร์",
"MoviesSelectedInterp": "{0} ภาพยนตร์ที่เลือก", "MoviesSelectedInterp": "{0} ภาพยนตร์ที่เลือก",
"MovieTitle": "ชื่อหนัง", "MovieTitle": "ชื่อหนัง",
"MovieTitleHelpText": "ชื่อของภาพยนตร์ที่จะไม่รวม (อาจมีความหมายอะไรก็ได้)", "MovieTitleToExcludeHelpText": "ชื่อของภาพยนตร์ที่จะไม่รวม (อาจมีความหมายอะไรก็ได้)",
"MovieYear": "ปีภาพยนตร์", "MovieYear": "ปีภาพยนตร์",
"MovieYearHelpText": "ปีของภาพยนตร์ที่จะไม่รวม", "MovieYearToExcludeHelpText": "ปีของภาพยนตร์ที่จะไม่รวม",
"MustContain": "ต้องมี", "MustContain": "ต้องมี",
"MustNotContain": "ต้องไม่มี", "MustNotContain": "ต้องไม่มี",
"NamingSettings": "การตั้งชื่อการตั้งค่า", "NamingSettings": "การตั้งชื่อการตั้งค่า",

@ -299,7 +299,7 @@
"ThisCannotBeCancelled": "Bu, {appName} yeniden başlatılmadan başlatıldıktan sonra iptal edilemez.", "ThisCannotBeCancelled": "Bu, {appName} yeniden başlatılmadan başlatıldıktan sonra iptal edilemez.",
"Title": "Başlık", "Title": "Başlık",
"TMDBId": "TMDb Kimliği", "TMDBId": "TMDb Kimliği",
"TmdbIdHelpText": "Hariç tutulacak filmin TMDb Kimliği", "TmdbIdExcludeHelpText": "Hariç tutulacak filmin TMDb Kimliği",
"Today": "Bugün", "Today": "Bugün",
"TorrentDelay": "Torrent Gecikmesi", "TorrentDelay": "Torrent Gecikmesi",
"TorrentDelayHelpText": "Bir torrent almadan önce beklemek için dakikalar içinde gecikme", "TorrentDelayHelpText": "Bir torrent almadan önce beklemek için dakikalar içinde gecikme",
@ -571,9 +571,9 @@
"MovieIsDownloading": "Film indiriliyor", "MovieIsDownloading": "Film indiriliyor",
"MovieIsUnmonitored": "Film takip edilmiyor", "MovieIsUnmonitored": "Film takip edilmiyor",
"MoviesSelectedInterp": "{count} Film Seçildi", "MoviesSelectedInterp": "{count} Film Seçildi",
"MovieTitleHelpText": "Hariç tutulacak filmin başlığı (anlamlı herhangi bir şey olabilir)", "MovieTitleToExcludeHelpText": "Hariç tutulacak filmin başlığı (anlamlı herhangi bir şey olabilir)",
"MovieYear": "Film Yılı", "MovieYear": "Film Yılı",
"MovieYearHelpText": "Hariç tutulacak film yılı", "MovieYearToExcludeHelpText": "Hariç tutulacak film yılı",
"PendingChangesDiscardChanges": "Değişiklikleri atın ve ayrıl", "PendingChangesDiscardChanges": "Değişiklikleri atın ve ayrıl",
"PreferredSize": "Tercih Edilen Boyut", "PreferredSize": "Tercih Edilen Boyut",
"Proper": "Uygun", "Proper": "Uygun",

@ -871,7 +871,7 @@
"Timeleft": "Час залишився", "Timeleft": "Час залишився",
"Title": "Назва", "Title": "Назва",
"Titles": "Назви", "Titles": "Назви",
"TmdbIdHelpText": "Ідентифікатор TMDb фільму, який потрібно виключити", "TmdbIdExcludeHelpText": "Ідентифікатор TMDb фільму, який потрібно виключити",
"TmdbRating": "Рейтинг TMDb", "TmdbRating": "Рейтинг TMDb",
"TmdbVotes": "Голоси TMDb", "TmdbVotes": "Голоси TMDb",
"TorrentDelayTime": "Затримка торрента: {0}", "TorrentDelayTime": "Затримка торрента: {0}",
@ -941,9 +941,9 @@
"InstallLatest": "Встановити останній", "InstallLatest": "Встановити останній",
"MegabytesPerMinute": "Мегабайт за хвилину", "MegabytesPerMinute": "Мегабайт за хвилину",
"Message": "Повідомлення", "Message": "Повідомлення",
"MovieTitleHelpText": "Назва фільму, який потрібно виключити (може бути будь-яким значущим)", "MovieTitleToExcludeHelpText": "Назва фільму, який потрібно виключити (може бути будь-яким значущим)",
"MovieYear": "Фільм рік", "MovieYear": "Фільм рік",
"MovieYearHelpText": "Рік фільму виключити", "MovieYearToExcludeHelpText": "Рік фільму виключити",
"NegateHelpText": "Якщо позначено, настроюваний формат не застосовуватиметься, якщо ця умова {0} збігається.", "NegateHelpText": "Якщо позначено, настроюваний формат не застосовуватиметься, якщо ця умова {0} збігається.",
"Never": "Ніколи", "Never": "Ніколи",
"New": "Новий", "New": "Новий",

@ -469,9 +469,9 @@
"AddMovies": "Thêm phim", "AddMovies": "Thêm phim",
"MoviesSelectedInterp": "{0} Phim đã chọn", "MoviesSelectedInterp": "{0} Phim đã chọn",
"MovieTitle": "Tên phim", "MovieTitle": "Tên phim",
"MovieTitleHelpText": "Tiêu đề của bộ phim cần loại trừ (có thể là bất kỳ điều gì có ý nghĩa)", "MovieTitleToExcludeHelpText": "Tiêu đề của bộ phim cần loại trừ (có thể là bất kỳ điều gì có ý nghĩa)",
"MovieYear": "Năm phim", "MovieYear": "Năm phim",
"MovieYearHelpText": "Năm của bộ phim để loại trừ", "MovieYearToExcludeHelpText": "Năm của bộ phim để loại trừ",
"MustContain": "Phải chứa", "MustContain": "Phải chứa",
"AddNewTmdbIdMessage": "Bạn cũng có thể tìm kiếm bằng cách sử dụng TMDb Id của một bộ phim. ví dụ. 'tmdb: 71663'", "AddNewTmdbIdMessage": "Bạn cũng có thể tìm kiếm bằng cách sử dụng TMDb Id của một bộ phim. ví dụ. 'tmdb: 71663'",
"Indexer": "Người lập chỉ mục", "Indexer": "Người lập chỉ mục",
@ -886,7 +886,7 @@
"Time": "Thời gian", "Time": "Thời gian",
"Title": "Tiêu đề", "Title": "Tiêu đề",
"TMDBId": "Id TMDb", "TMDBId": "Id TMDb",
"TmdbIdHelpText": "Id TMDb của phim để loại trừ", "TmdbIdExcludeHelpText": "Id TMDb của phim để loại trừ",
"TorrentDelayTime": "Độ trễ Torrent: {0}", "TorrentDelayTime": "Độ trễ Torrent: {0}",
"Torrents": "Torrents", "Torrents": "Torrents",
"Trailer": "Giới thiệu tóm tắt", "Trailer": "Giới thiệu tóm tắt",

@ -837,7 +837,7 @@
"TagsSettingsSummary": "显示全部标签和标签使用情况,可删除未使用的标签", "TagsSettingsSummary": "显示全部标签和标签使用情况,可删除未使用的标签",
"TMDb": "TMDb", "TMDb": "TMDb",
"TMDBId": "TMDb ID", "TMDBId": "TMDb ID",
"TmdbIdHelpText": "排除电影的TMDb ID", "TmdbIdExcludeHelpText": "排除电影的TMDb ID",
"TorrentDelay": "Torrent延时", "TorrentDelay": "Torrent延时",
"TorrentDelayTime": "Torrent延时{0}", "TorrentDelayTime": "Torrent延时{0}",
"Torrents": "种子", "Torrents": "种子",
@ -901,8 +901,8 @@
"NextExecution": "接下来执行", "NextExecution": "接下来执行",
"New": "新的", "New": "新的",
"NegateHelpText": "如勾选,当条件 {0} 满足时不会应用自定义格式。", "NegateHelpText": "如勾选,当条件 {0} 满足时不会应用自定义格式。",
"MovieYearHelpText": "排除的电影的年份", "MovieYearToExcludeHelpText": "排除的电影的年份",
"MovieTitleHelpText": "要排除的电影标题(可以是任何字段)", "MovieTitleToExcludeHelpText": "要排除的电影标题(可以是任何字段)",
"MovieIsRecommend": "影片是根据最近添加的推荐", "MovieIsRecommend": "影片是根据最近添加的推荐",
"MovieIndexScrollTop": "影片索引:滚动到顶部", "MovieIndexScrollTop": "影片索引:滚动到顶部",
"MovieIndexScrollBottom": "影片索引:滚动到底部", "MovieIndexScrollBottom": "影片索引:滚动到底部",

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation; using FluentValidation;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NzbDrone.Core.ImportLists.ImportExclusions; using NzbDrone.Core.ImportLists.ImportExclusions;
using Radarr.Http; using Radarr.Http;
using Radarr.Http.Extensions;
using Radarr.Http.REST; using Radarr.Http.REST;
using Radarr.Http.REST.Attributes; using Radarr.Http.REST.Attributes;
@ -25,6 +27,7 @@ namespace Radarr.Api.V3.ImportLists
[HttpGet] [HttpGet]
[Produces("application/json")] [Produces("application/json")]
[Obsolete("Deprecated")]
public List<ImportListExclusionResource> GetImportListExclusions() public List<ImportListExclusionResource> GetImportListExclusions()
{ {
return _importListExclusionService.All().ToResource(); return _importListExclusionService.All().ToResource();
@ -35,6 +38,16 @@ namespace Radarr.Api.V3.ImportLists
return _importListExclusionService.Get(id).ToResource(); return _importListExclusionService.Get(id).ToResource();
} }
[HttpGet("paged")]
[Produces("application/json")]
public PagingResource<ImportListExclusionResource> GetImportListExclusionsPaged([FromQuery] PagingRequestResource paging)
{
var pagingResource = new PagingResource<ImportListExclusionResource>(paging);
var pageSpec = pagingResource.MapToPagingSpec<ImportListExclusionResource, ImportListExclusion>();
return pageSpec.ApplyToPage(_importListExclusionService.Paged, ImportListExclusionResourceMapper.ToResource);
}
[RestPostById] [RestPostById]
[Consumes("application/json")] [Consumes("application/json")]
public ActionResult<ImportListExclusionResource> AddImportListExclusion([FromBody] ImportListExclusionResource resource) public ActionResult<ImportListExclusionResource> AddImportListExclusion([FromBody] ImportListExclusionResource resource)

Loading…
Cancel
Save