mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-17 16:14:46 -04:00
Compare commits
60 Commits
slack-refa
...
move-rpm-i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12ae4fba88 | ||
|
|
194e0f3d7f | ||
|
|
d1fa92bc6c | ||
|
|
974e44ce48 | ||
|
|
de05be62d7 | ||
|
|
cae5badee0 | ||
|
|
45d8227654 | ||
|
|
7bbd2246c4 | ||
|
|
59fed13442 | ||
|
|
50b273acae | ||
|
|
4278415fd7 | ||
|
|
124b50288d | ||
|
|
3fcc395964 | ||
|
|
0ee9981cba | ||
|
|
2848899206 | ||
|
|
f1a00764cd | ||
|
|
346236764c | ||
|
|
eecd4e4b7d | ||
|
|
2838d8ca29 | ||
|
|
4ebcbc28aa | ||
|
|
2c24f7ca04 | ||
|
|
ec86de78d2 | ||
|
|
4f5f9ff77e | ||
|
|
465bb403a9 | ||
|
|
9e175e28ef | ||
|
|
4d2a311e40 | ||
|
|
b2195148a2 | ||
|
|
2ae7371d73 | ||
|
|
7b03a856c9 | ||
|
|
48d1d47b67 | ||
|
|
906b9bb92a | ||
|
|
6fc14278e6 | ||
|
|
34b269086d | ||
|
|
6c40a27f2e | ||
|
|
be158a09b4 | ||
|
|
eecd746f51 | ||
|
|
5ed034320e | ||
|
|
41dd678dfd | ||
|
|
716eadc551 | ||
|
|
1cb31aa95c | ||
|
|
568dd2fbb2 | ||
|
|
5d091e519e | ||
|
|
faab78c00a | ||
|
|
f1461056ce | ||
|
|
2042ffce62 | ||
|
|
c6ae6f7b1c | ||
|
|
8d7affae68 | ||
|
|
a418111245 | ||
|
|
6359ed5757 | ||
|
|
9a8c1d7d1b | ||
|
|
e89c2ee9f7 | ||
|
|
3d36f88939 | ||
|
|
8e4320a93b | ||
|
|
b095676010 | ||
|
|
41d69d8484 | ||
|
|
073e59e3db | ||
|
|
588a0843a4 | ||
|
|
26cedfd47d | ||
|
|
16789e5b6b | ||
|
|
159edcde94 |
@@ -120,7 +120,7 @@ class Blocklist extends Component {
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label="Remove Selected"
|
||||
label={translate('RemoveSelected')}
|
||||
iconName={icons.REMOVE}
|
||||
isDisabled={!selectedIds.length}
|
||||
isSpinning={isRemoving}
|
||||
|
||||
@@ -161,7 +161,7 @@ class AddNewMovie extends Component {
|
||||
{translate('YouCanAlsoSearch')}
|
||||
</div>
|
||||
<div>
|
||||
<Link to="https://wiki.servarr.com/radarr/faq#why-cant-i-add-a-new-movie-to-radarr">
|
||||
<Link to="https://wiki.servarr.com/radarr/faq#why-can-i-not-add-a-new-movie-to-radarr">
|
||||
{translate('CantFindMovie')}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -43,15 +43,15 @@ class CalendarEvent extends Component {
|
||||
const link = `/movie/${titleSlug}`;
|
||||
const eventType = [];
|
||||
|
||||
if (moment(date).isSame(moment(inCinemas), 'day')) {
|
||||
if (inCinemas && moment(date).isSame(moment(inCinemas), 'day')) {
|
||||
eventType.push('Cinemas');
|
||||
}
|
||||
|
||||
if (moment(date).isSame(moment(physicalRelease), 'day')) {
|
||||
if (physicalRelease && moment(date).isSame(moment(physicalRelease), 'day')) {
|
||||
eventType.push('Physical');
|
||||
}
|
||||
|
||||
if (moment(date).isSame(moment(digitalRelease), 'day')) {
|
||||
if (digitalRelease && moment(date).isSame(moment(digitalRelease), 'day')) {
|
||||
eventType.push('Digital');
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,9 @@ class FilterBuilderModalContent extends Component {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.label}>Filters</div>
|
||||
<div className={styles.label}>
|
||||
{translate('Filters')}
|
||||
</div>
|
||||
|
||||
<div className={styles.rows}>
|
||||
{
|
||||
|
||||
@@ -6,8 +6,7 @@ import SelectInput from './SelectInput';
|
||||
const availabilityOptions = [
|
||||
{ key: 'announced', value: translate('Announced') },
|
||||
{ key: 'inCinemas', value: translate('InCinemas') },
|
||||
{ key: 'released', value: translate('Released') },
|
||||
{ key: 'preDB', value: translate('PreDB') }
|
||||
{ key: 'released', value: translate('Released') }
|
||||
];
|
||||
|
||||
function AvailabilitySelectInput(props) {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchDownloadClients } from 'Store/Actions/settingsActions';
|
||||
import sortByName from 'Utilities/Array/sortByName';
|
||||
import EnhancedSelectInput from './EnhancedSelectInput';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.settings.downloadClients,
|
||||
(state, { includeAny }) => includeAny,
|
||||
(state, { protocol }) => protocol,
|
||||
(downloadClients, includeAny, protocolFilter) => {
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
items
|
||||
} = downloadClients;
|
||||
|
||||
const filteredItems = items.filter((item) => item.protocol === protocolFilter);
|
||||
|
||||
const values = _.map(filteredItems.sort(sortByName), (downloadClient) => {
|
||||
return {
|
||||
key: downloadClient.id,
|
||||
value: downloadClient.name
|
||||
};
|
||||
});
|
||||
|
||||
if (includeAny) {
|
||||
values.unshift({
|
||||
key: 0,
|
||||
value: '(Any)'
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
values
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
dispatchFetchDownloadClients: fetchDownloadClients
|
||||
};
|
||||
|
||||
class DownloadClientSelectInputConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.props.isPopulated) {
|
||||
this.props.dispatchFetchDownloadClients();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onChange = ({ name, value }) => {
|
||||
this.props.onChange({ name, value: parseInt(value) });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EnhancedSelectInput
|
||||
{...this.props}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DownloadClientSelectInputConnector.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
|
||||
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
includeAny: PropTypes.bool.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
dispatchFetchDownloadClients: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
DownloadClientSelectInputConnector.defaultProps = {
|
||||
includeAny: false,
|
||||
protocol: 'torrent'
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(DownloadClientSelectInputConnector);
|
||||
@@ -8,6 +8,7 @@ import AvailabilitySelectInput from './AvailabilitySelectInput';
|
||||
import CaptchaInputConnector from './CaptchaInputConnector';
|
||||
import CheckInput from './CheckInput';
|
||||
import DeviceInputConnector from './DeviceInputConnector';
|
||||
import DownloadClientSelectInputConnector from './DownloadClientSelectInputConnector';
|
||||
import EnhancedSelectInput from './EnhancedSelectInput';
|
||||
import EnhancedSelectInputConnector from './EnhancedSelectInputConnector';
|
||||
import FormInputHelpText from './FormInputHelpText';
|
||||
@@ -73,6 +74,9 @@ function getComponent(type) {
|
||||
case inputTypes.INDEXER_FLAGS_SELECT:
|
||||
return IndexerFlagsSelectInputConnector;
|
||||
|
||||
case inputTypes.DOWNLOAD_CLIENT_SELECT:
|
||||
return DownloadClientSelectInputConnector;
|
||||
|
||||
case inputTypes.LANGUAGE_SELECT:
|
||||
return LanguageSelectInputConnector;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ export const QUALITY_PROFILE_SELECT = 'qualityProfileSelect';
|
||||
export const ROOT_FOLDER_SELECT = 'rootFolderSelect';
|
||||
export const INDEXER_FLAGS_SELECT = 'indexerFlagsSelect';
|
||||
export const LANGUAGE_SELECT = 'languageSelect';
|
||||
export const DOWNLOAD_CLIENT_SELECT = 'downloadClientSelect';
|
||||
export const SELECT = 'select';
|
||||
export const DYNAMIC_SELECT = 'dynamicSelect';
|
||||
export const TAG = 'tag';
|
||||
@@ -35,6 +36,7 @@ export const all = [
|
||||
PASSWORD,
|
||||
PATH,
|
||||
QUALITY_PROFILE_SELECT,
|
||||
DOWNLOAD_CLIENT_SELECT,
|
||||
ROOT_FOLDER_SELECT,
|
||||
INDEXER_FLAGS_SELECT,
|
||||
LANGUAGE_SELECT,
|
||||
|
||||
@@ -19,6 +19,7 @@ import { align, icons, kinds, scrollDirections } from 'Helpers/Props';
|
||||
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
|
||||
import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
|
||||
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
||||
import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
|
||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
@@ -40,6 +41,11 @@ const columns = [
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'releaseGroup',
|
||||
label: translate('ReleaseGroup'),
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'quality',
|
||||
label: translate('Quality'),
|
||||
@@ -83,6 +89,7 @@ const SELECT = 'select';
|
||||
const MOVIE = 'movie';
|
||||
const LANGUAGE = 'language';
|
||||
const QUALITY = 'quality';
|
||||
const RELEASE_GROUP = 'releaseGroup';
|
||||
|
||||
class InteractiveImportModalContent extends Component {
|
||||
|
||||
@@ -202,10 +209,11 @@ class InteractiveImportModalContent extends Component {
|
||||
const errorMessage = getErrorMessage(error, translate('UnableToLoadManualImportItems'));
|
||||
|
||||
const bulkSelectOptions = [
|
||||
{
|
||||
key: SELECT, value: translate('SelectDotDot'), disabled: true },
|
||||
{ key: SELECT, value: translate('SelectDotDot'), disabled: true },
|
||||
{ key: LANGUAGE, value: translate('SelectLanguage') },
|
||||
{ key: QUALITY, value: translate('SelectQuality') }];
|
||||
{ key: QUALITY, value: translate('SelectQuality') },
|
||||
{ key: RELEASE_GROUP, value: translate('SelectReleaseGroup') }
|
||||
];
|
||||
|
||||
if (allowMovieChange) {
|
||||
bulkSelectOptions.splice(1, 0, {
|
||||
@@ -372,6 +380,13 @@ class InteractiveImportModalContent extends Component {
|
||||
real={false}
|
||||
onModalClose={this.onSelectModalClose}
|
||||
/>
|
||||
|
||||
<SelectReleaseGroupModal
|
||||
isOpen={selectModalOpen === RELEASE_GROUP}
|
||||
ids={selectedIds}
|
||||
releaseGroup=""
|
||||
onModalClose={this.onSelectModalClose}
|
||||
/>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,7 +110,8 @@ class InteractiveImportModalContentConnector extends Component {
|
||||
const {
|
||||
movie,
|
||||
quality,
|
||||
languages
|
||||
languages,
|
||||
releaseGroup
|
||||
} = item;
|
||||
|
||||
if (!movie) {
|
||||
@@ -132,6 +133,7 @@ class InteractiveImportModalContentConnector extends Component {
|
||||
path: item.path,
|
||||
folderName: item.folderName,
|
||||
movieId: movie.id,
|
||||
releaseGroup,
|
||||
quality,
|
||||
languages,
|
||||
downloadId: this.props.downloadId
|
||||
|
||||
@@ -11,6 +11,7 @@ import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
|
||||
import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
|
||||
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
||||
import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
|
||||
import MovieLanguage from 'Movie/MovieLanguage';
|
||||
import MovieQuality from 'Movie/MovieQuality';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
@@ -28,6 +29,7 @@ class InteractiveImportRow extends Component {
|
||||
|
||||
this.state = {
|
||||
isSelectMovieModalOpen: false,
|
||||
isSelectReleaseGroupModalOpen: false,
|
||||
isSelectQualityModalOpen: false,
|
||||
isSelectLanguageModalOpen: false
|
||||
};
|
||||
@@ -103,6 +105,10 @@ class InteractiveImportRow extends Component {
|
||||
this.setState({ isSelectMovieModalOpen: true });
|
||||
}
|
||||
|
||||
onSelectReleaseGroupPress = () => {
|
||||
this.setState({ isSelectReleaseGroupModalOpen: true });
|
||||
}
|
||||
|
||||
onSelectQualityPress = () => {
|
||||
this.setState({ isSelectQualityModalOpen: true });
|
||||
}
|
||||
@@ -116,6 +122,11 @@ class InteractiveImportRow extends Component {
|
||||
this.selectRowAfterChange(changed);
|
||||
}
|
||||
|
||||
onSelectReleaseGroupModalClose = (changed) => {
|
||||
this.setState({ isSelectReleaseGroupModalOpen: false });
|
||||
this.selectRowAfterChange(changed);
|
||||
}
|
||||
|
||||
onSelectQualityModalClose = (changed) => {
|
||||
this.setState({ isSelectQualityModalOpen: false });
|
||||
this.selectRowAfterChange(changed);
|
||||
@@ -137,6 +148,7 @@ class InteractiveImportRow extends Component {
|
||||
movie,
|
||||
quality,
|
||||
languages,
|
||||
releaseGroup,
|
||||
size,
|
||||
rejections,
|
||||
isReprocessing,
|
||||
@@ -147,7 +159,8 @@ class InteractiveImportRow extends Component {
|
||||
const {
|
||||
isSelectMovieModalOpen,
|
||||
isSelectQualityModalOpen,
|
||||
isSelectLanguageModalOpen
|
||||
isSelectLanguageModalOpen,
|
||||
isSelectReleaseGroupModalOpen
|
||||
} = this.state;
|
||||
|
||||
const movieTitle = movie ? movie.title + ( movie.year > 0 ? ` (${movie.year})` : '') : '';
|
||||
@@ -155,6 +168,7 @@ class InteractiveImportRow extends Component {
|
||||
const showMoviePlaceholder = isSelected && !movie;
|
||||
const showQualityPlaceholder = isSelected && !quality;
|
||||
const showLanguagePlaceholder = isSelected && !languages && !isReprocessing;
|
||||
const showReleaseGroupPlaceholder = isSelected && !releaseGroup;
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
@@ -181,6 +195,17 @@ class InteractiveImportRow extends Component {
|
||||
}
|
||||
</TableRowCellButton>
|
||||
|
||||
<TableRowCellButton
|
||||
title={translate('ClickToChangeReleaseGroup')}
|
||||
onPress={this.onSelectReleaseGroupPress}
|
||||
>
|
||||
{
|
||||
showReleaseGroupPlaceholder ?
|
||||
<InteractiveImportRowCellPlaceholder /> :
|
||||
releaseGroup
|
||||
}
|
||||
</TableRowCellButton>
|
||||
|
||||
<TableRowCellButton
|
||||
className={styles.quality}
|
||||
title={translate('ClickToChangeQuality')}
|
||||
@@ -268,6 +293,13 @@ class InteractiveImportRow extends Component {
|
||||
onModalClose={this.onSelectMovieModalClose}
|
||||
/>
|
||||
|
||||
<SelectReleaseGroupModal
|
||||
isOpen={isSelectReleaseGroupModalOpen}
|
||||
ids={[id]}
|
||||
releaseGroup={releaseGroup ?? ''}
|
||||
onModalClose={this.onSelectReleaseGroupModalClose}
|
||||
/>
|
||||
|
||||
<SelectQualityModal
|
||||
isOpen={isSelectQualityModalOpen}
|
||||
ids={[id]}
|
||||
@@ -296,6 +328,7 @@ InteractiveImportRow.propTypes = {
|
||||
movie: PropTypes.object,
|
||||
quality: PropTypes.object,
|
||||
languages: PropTypes.arrayOf(PropTypes.object),
|
||||
releaseGroup: PropTypes.string,
|
||||
size: PropTypes.number.isRequired,
|
||||
rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
isReprocessing: PropTypes.bool,
|
||||
|
||||
@@ -128,7 +128,7 @@ class SelectLanguageModalContent extends Component {
|
||||
kind={kinds.SUCCESS}
|
||||
onPress={this.onLanguageSelect}
|
||||
>
|
||||
{translate('SelectLanguges')}
|
||||
{translate('SelectLanguages')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import SelectReleaseGroupModalContentConnector from './SelectReleaseGroupModalContentConnector';
|
||||
|
||||
class SelectReleaseGroupModal extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isOpen,
|
||||
onModalClose,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<SelectReleaseGroupModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SelectReleaseGroupModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SelectReleaseGroupModal;
|
||||
@@ -0,0 +1,99 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
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 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';
|
||||
|
||||
class SelectReleaseGroupModalContent extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
const {
|
||||
releaseGroup
|
||||
} = props;
|
||||
|
||||
this.state = {
|
||||
releaseGroup
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onReleaseGroupChange = ({ value }) => {
|
||||
this.setState({ releaseGroup: value });
|
||||
}
|
||||
|
||||
onReleaseGroupSelect = () => {
|
||||
this.props.onReleaseGroupSelect(this.state);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
onModalClose
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
releaseGroup
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{translate('ManualImportSetReleaseGroup')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<Form>
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('ReleaseGroup')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="releaseGroup"
|
||||
value={releaseGroup}
|
||||
onChange={this.onReleaseGroupChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={onModalClose}>
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
kind={kinds.SUCCESS}
|
||||
onPress={this.onReleaseGroupSelect}
|
||||
>
|
||||
{translate('SetReleaseGroup')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SelectReleaseGroupModalContent.propTypes = {
|
||||
releaseGroup: PropTypes.string.isRequired,
|
||||
onReleaseGroupSelect: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SelectReleaseGroupModalContent;
|
||||
@@ -0,0 +1,54 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { reprocessInteractiveImportItems, updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
|
||||
import SelectReleaseGroupModalContent from './SelectReleaseGroupModalContent';
|
||||
|
||||
const mapDispatchToProps = {
|
||||
dispatchUpdateInteractiveImportItems: updateInteractiveImportItems,
|
||||
dispatchReprocessInteractiveImportItems: reprocessInteractiveImportItems
|
||||
};
|
||||
|
||||
class SelectReleaseGroupModalContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onReleaseGroupSelect = ({ releaseGroup }) => {
|
||||
const {
|
||||
ids,
|
||||
dispatchUpdateInteractiveImportItems,
|
||||
dispatchReprocessInteractiveImportItems
|
||||
} = this.props;
|
||||
|
||||
dispatchUpdateInteractiveImportItems({
|
||||
ids,
|
||||
releaseGroup
|
||||
});
|
||||
|
||||
dispatchReprocessInteractiveImportItems({ ids });
|
||||
|
||||
this.props.onModalClose(true);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SelectReleaseGroupModalContent
|
||||
{...this.props}
|
||||
onReleaseGroupSelect={this.onReleaseGroupSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SelectReleaseGroupModalContentConnector.propTypes = {
|
||||
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
|
||||
dispatchReprocessInteractiveImportItems: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(null, mapDispatchToProps)(SelectReleaseGroupModalContentConnector);
|
||||
@@ -155,7 +155,7 @@ DeleteMovieModalContent.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
hasFile: PropTypes.bool.isRequired,
|
||||
sizeOnDisk: PropTypes.string.isRequired,
|
||||
sizeOnDisk: PropTypes.number.isRequired,
|
||||
onDeletePress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -101,6 +101,15 @@ function MovieIndexSortMenu(props) {
|
||||
{translate('DigitalRelease')}
|
||||
</SortMenuItem>
|
||||
|
||||
<SortMenuItem
|
||||
name="ratings"
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
onPress={onSortSelect}
|
||||
>
|
||||
{translate('Ratings')}
|
||||
</SortMenuItem>
|
||||
|
||||
<SortMenuItem
|
||||
name="path"
|
||||
sortKey={sortKey}
|
||||
|
||||
@@ -6,7 +6,7 @@ export function getMovieStatusDetails(status) {
|
||||
let statusDetails = {
|
||||
icon: icons.ANNOUNCED,
|
||||
title: translate('Announced'),
|
||||
message: translate('AnnoucedMsg')
|
||||
message: translate('AnnouncedMsg')
|
||||
};
|
||||
|
||||
if (status === 'deleted') {
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.rightButtons {
|
||||
justify-content: flex-end;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.addSpecification {
|
||||
composes: customFormat from '~./CustomFormat.css';
|
||||
|
||||
|
||||
@@ -198,26 +198,25 @@ class EditCustomFormatModalContent extends Component {
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
{
|
||||
id &&
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
kind={kinds.DANGER}
|
||||
onPress={onDeleteCustomFormatPress}
|
||||
>
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
<div className={styles.rightButtons}>
|
||||
{
|
||||
id &&
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
kind={kinds.DANGER}
|
||||
onPress={onDeleteCustomFormatPress}
|
||||
>
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
{
|
||||
!id &&
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
onPress={this.onImportPress}
|
||||
>
|
||||
{translate('Import')}
|
||||
</Button>
|
||||
}
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
onPress={this.onImportPress}
|
||||
>
|
||||
{translate('Import')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
.remotePathMapping {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
align-items: left;
|
||||
margin-bottom: 10px;
|
||||
height: 30px;
|
||||
border-bottom: 1px solid $borderColor;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
flex: 0 0 25px;
|
||||
}
|
||||
|
||||
.host {
|
||||
flex: 0 0 300px;
|
||||
flex: 1 0 300px;
|
||||
}
|
||||
|
||||
.path {
|
||||
flex: 0 0 400px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex: 1 0 auto;
|
||||
padding-right: 10px;
|
||||
flex: 1 0 400px;
|
||||
}
|
||||
|
||||
@@ -66,9 +66,6 @@ class RemotePathMapping extends Component {
|
||||
styles.remotePathMapping
|
||||
)}
|
||||
>
|
||||
<div className={styles.host}>{host}</div>
|
||||
<div className={styles.path}>{remotePath}</div>
|
||||
<div className={styles.path}>{localPath}</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<Link
|
||||
@@ -78,6 +75,10 @@ class RemotePathMapping extends Component {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className={styles.host}>{host}</div>
|
||||
<div className={styles.path}>{remotePath}</div>
|
||||
<div className={styles.path}>{localPath}</div>
|
||||
|
||||
<EditRemotePathMappingModalConnector
|
||||
id={id}
|
||||
isOpen={this.state.isEditRemotePathMappingModalOpen}
|
||||
|
||||
@@ -4,17 +4,19 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.actions {
|
||||
flex: 0 0 25px;
|
||||
}
|
||||
|
||||
.host {
|
||||
flex: 0 0 300px;
|
||||
flex: 1 0 300px;
|
||||
}
|
||||
|
||||
.path {
|
||||
flex: 0 0 400px;
|
||||
flex: 1 0 400px;
|
||||
}
|
||||
|
||||
.addRemotePathMapping {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,9 +51,15 @@ class RemotePathMappings extends Component {
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.remotePathMappingsHeader}>
|
||||
<div className={styles.host}>Host</div>
|
||||
<div className={styles.path}>Remote Path</div>
|
||||
<div className={styles.path}>Local Path</div>
|
||||
<div className={styles.host}>
|
||||
{translate('Host')}
|
||||
</div>
|
||||
<div className={styles.path}>
|
||||
{translate('RemotePath')}
|
||||
</div>
|
||||
<div className={styles.path}>
|
||||
{translate('LocalPath')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -2,16 +2,16 @@ import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import EditImportExclusionModalContentConnector from './EditImportExclusionModalContentConnector';
|
||||
import EditImportListExclusionModalContentConnector from './EditImportListExclusionModalContentConnector';
|
||||
|
||||
function EditImportExclusionModal({ isOpen, onModalClose, ...otherProps }) {
|
||||
function EditImportListExclusionModal({ isOpen, onModalClose, ...otherProps }) {
|
||||
return (
|
||||
<Modal
|
||||
size={sizes.MEDIUM}
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<EditImportExclusionModalContentConnector
|
||||
<EditImportListExclusionModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
@@ -19,9 +19,9 @@ function EditImportExclusionModal({ isOpen, onModalClose, ...otherProps }) {
|
||||
);
|
||||
}
|
||||
|
||||
EditImportExclusionModal.propTypes = {
|
||||
EditImportListExclusionModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default EditImportExclusionModal;
|
||||
export default EditImportListExclusionModal;
|
||||
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||
import EditImportExclusionModal from './EditImportExclusionModal';
|
||||
import EditImportListExclusionModal from './EditImportListExclusionModal';
|
||||
|
||||
function mapStateToProps() {
|
||||
return {};
|
||||
@@ -12,7 +12,7 @@ const mapDispatchToProps = {
|
||||
clearPendingChanges
|
||||
};
|
||||
|
||||
class EditImportExclusionModalConnector extends Component {
|
||||
class EditImportListExclusionModalConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
@@ -27,7 +27,7 @@ class EditImportExclusionModalConnector extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EditImportExclusionModal
|
||||
<EditImportListExclusionModal
|
||||
{...this.props}
|
||||
onModalClose={this.onModalClose}
|
||||
/>
|
||||
@@ -35,9 +35,9 @@ class EditImportExclusionModalConnector extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
EditImportExclusionModalConnector.propTypes = {
|
||||
EditImportListExclusionModalConnector.propTypes = {
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
clearPendingChanges: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(EditImportExclusionModalConnector);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(EditImportListExclusionModalConnector);
|
||||
@@ -13,9 +13,9 @@ 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 './EditImportExclusionModalContent.css';
|
||||
import styles from './EditImportListExclusionModalContent.css';
|
||||
|
||||
function EditImportExclusionModalContent(props) {
|
||||
function EditImportListExclusionModalContent(props) {
|
||||
const {
|
||||
id,
|
||||
isFetching,
|
||||
@@ -130,7 +130,7 @@ function EditImportExclusionModalContent(props) {
|
||||
);
|
||||
}
|
||||
|
||||
EditImportExclusionModalContent.propTypes = {
|
||||
EditImportListExclusionModalContent.propTypes = {
|
||||
id: PropTypes.number,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
@@ -143,4 +143,4 @@ EditImportExclusionModalContent.propTypes = {
|
||||
onDeleteImportExclusionPress: PropTypes.func
|
||||
};
|
||||
|
||||
export default EditImportExclusionModalContent;
|
||||
export default EditImportListExclusionModalContent;
|
||||
@@ -5,7 +5,7 @@ import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { saveImportExclusion, setImportExclusionValue } from 'Store/Actions/settingsActions';
|
||||
import selectSettings from 'Store/Selectors/selectSettings';
|
||||
import EditImportExclusionModalContent from './EditImportExclusionModalContent';
|
||||
import EditImportListExclusionModalContent from './EditImportListExclusionModalContent';
|
||||
|
||||
const newImportExclusion = {
|
||||
movieTitle: '',
|
||||
@@ -97,7 +97,7 @@ class EditImportExclusionModalContentConnector extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EditImportExclusionModalContent
|
||||
<EditImportListExclusionModalContent
|
||||
{...this.props}
|
||||
onSavePress={this.onSavePress}
|
||||
onInputChange={this.onInputChange}
|
||||
@@ -8,12 +8,14 @@
|
||||
}
|
||||
|
||||
.movieTitle {
|
||||
flex: 0 0 400px;
|
||||
@add-mixin truncate;
|
||||
|
||||
flex: 0 0 600px;
|
||||
}
|
||||
|
||||
.tmdbId,
|
||||
.movieYear {
|
||||
flex: 0 0 200px;
|
||||
flex: 0 0 70px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@@ -6,10 +6,10 @@ 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 EditImportExclusionModalConnector from './EditImportExclusionModalConnector';
|
||||
import styles from './ImportExclusion.css';
|
||||
import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector';
|
||||
import styles from './ImportListExclusion.css';
|
||||
|
||||
class ImportExclusion extends Component {
|
||||
class ImportListExclusion extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -78,7 +78,7 @@ class ImportExclusion extends Component {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<EditImportExclusionModalConnector
|
||||
<EditImportListExclusionModalConnector
|
||||
id={id}
|
||||
isOpen={this.state.isEditImportExclusionModalOpen}
|
||||
onModalClose={this.onEditImportExclusionModalClose}
|
||||
@@ -99,7 +99,7 @@ class ImportExclusion extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ImportExclusion.propTypes = {
|
||||
ImportListExclusion.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
movieTitle: PropTypes.string.isRequired,
|
||||
tmdbId: PropTypes.number.isRequired,
|
||||
@@ -107,9 +107,9 @@ ImportExclusion.propTypes = {
|
||||
onConfirmDeleteImportExclusion: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
ImportExclusion.defaultProps = {
|
||||
ImportListExclusion.defaultProps = {
|
||||
// The drag preview will not connect the drag handle.
|
||||
connectDragSource: (node) => node
|
||||
};
|
||||
|
||||
export default ImportExclusion;
|
||||
export default ImportListExclusion;
|
||||
@@ -1,16 +1,16 @@
|
||||
.importExclusionsHeader {
|
||||
.importListExclusionsHeader {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.title {
|
||||
flex: 0 0 400px;
|
||||
flex: 0 0 600px;
|
||||
}
|
||||
|
||||
.tmdbId,
|
||||
.movieYear {
|
||||
flex: 0 0 200px;
|
||||
flex: 0 0 70px;
|
||||
}
|
||||
|
||||
.addImportExclusion {
|
||||
@@ -6,11 +6,11 @@ import Link from 'Components/Link/Link';
|
||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditImportExclusionModalConnector from './EditImportExclusionModalConnector';
|
||||
import ImportExclusion from './ImportExclusion';
|
||||
import styles from './ImportExclusions.css';
|
||||
import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector';
|
||||
import ImportListExclusion from './ImportListExclusion';
|
||||
import styles from './ImportListExclusions.css';
|
||||
|
||||
class ImportExclusions extends Component {
|
||||
class ImportListExclusions extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -51,16 +51,22 @@ class ImportExclusions extends Component {
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.importExclusionsHeader}>
|
||||
<div className={styles.tmdbId}>TMDB Id</div>
|
||||
<div className={styles.title}>Title</div>
|
||||
<div className={styles.movieYear}>Year</div>
|
||||
<div className={styles.tmdbId}>
|
||||
TMDb Id
|
||||
</div>
|
||||
<div className={styles.title}>
|
||||
{translate('Title')}
|
||||
</div>
|
||||
<div className={styles.movieYear}>
|
||||
{translate('Year')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{
|
||||
items.map((item, index) => {
|
||||
return (
|
||||
<ImportExclusion
|
||||
<ImportListExclusion
|
||||
key={item.id}
|
||||
{...item}
|
||||
{...otherProps}
|
||||
@@ -81,7 +87,7 @@ class ImportExclusions extends Component {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<EditImportExclusionModalConnector
|
||||
<EditImportListExclusionModalConnector
|
||||
isOpen={this.state.isAddImportExclusionModalOpen}
|
||||
onModalClose={this.onModalClose}
|
||||
/>
|
||||
@@ -92,11 +98,11 @@ class ImportExclusions extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ImportExclusions.propTypes = {
|
||||
ImportListExclusions.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onConfirmDeleteImportExclusion: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ImportExclusions;
|
||||
export default ImportListExclusions;
|
||||
@@ -3,7 +3,7 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { deleteImportExclusion, fetchImportExclusions } from 'Store/Actions/settingsActions';
|
||||
import ImportExclusions from './ImportExclusions';
|
||||
import ImportListExclusions from './ImportListExclusions';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
@@ -42,7 +42,7 @@ class ImportExclusionsConnector extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ImportExclusions
|
||||
<ImportListExclusions
|
||||
{...this.state}
|
||||
{...this.props}
|
||||
onConfirmDeleteImportExclusion={this.onConfirmDeleteImportExclusion}
|
||||
@@ -7,7 +7,7 @@ import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import ImportExclusionsConnector from './ImportExclusions/ImportExclusionsConnector';
|
||||
import ImportListExclusionsConnector from './ImportListExclusions/ImportListExclusionsConnector';
|
||||
import ImportListsConnector from './ImportLists/ImportListsConnector';
|
||||
import ImportListOptionsConnector from './Options/ImportListOptionsConnector';
|
||||
|
||||
@@ -86,7 +86,7 @@ class ImportListSettings extends Component {
|
||||
onChildStateChange={this.onChildStateChange}
|
||||
/>
|
||||
|
||||
<ImportExclusionsConnector />
|
||||
<ImportListExclusionsConnector />
|
||||
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
|
||||
@@ -45,7 +45,9 @@ function EditIndexerModalContent(props) {
|
||||
supportsSearch,
|
||||
tags,
|
||||
fields,
|
||||
priority
|
||||
priority,
|
||||
protocol,
|
||||
downloadClientId
|
||||
} = item;
|
||||
|
||||
return (
|
||||
@@ -154,8 +156,25 @@ function EditIndexerModalContent(props) {
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('DownloadClient')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.DOWNLOAD_CLIENT_SELECT}
|
||||
name="downloadClientId"
|
||||
helpText={translate('IndexerDownloadClientHelpText')}
|
||||
{...downloadClientId}
|
||||
includeAny={true}
|
||||
protocol={protocol.value}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Tags</FormLabel>
|
||||
<FormLabel>{translate('Tags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
|
||||
@@ -63,6 +63,7 @@ class Notification extends Component {
|
||||
onMovieFileDelete,
|
||||
onMovieFileDeleteForUpgrade,
|
||||
onHealthIssue,
|
||||
onApplicationUpdate,
|
||||
supportsOnGrab,
|
||||
supportsOnDownload,
|
||||
supportsOnUpgrade,
|
||||
@@ -70,7 +71,8 @@ class Notification extends Component {
|
||||
supportsOnMovieDelete,
|
||||
supportsOnMovieFileDelete,
|
||||
supportsOnMovieFileDeleteForUpgrade,
|
||||
supportsOnHealthIssue
|
||||
supportsOnHealthIssue,
|
||||
supportsOnApplicationUpdate
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -123,6 +125,14 @@ class Notification extends Component {
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnApplicationUpdate && onApplicationUpdate ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnApplicationUpdate')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnMovieDelete && onMovieDelete ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
@@ -148,7 +158,7 @@ class Notification extends Component {
|
||||
}
|
||||
|
||||
{
|
||||
!onGrab && !onDownload && !onRename && !onHealthIssue && !onMovieDelete && !onMovieFileDelete ?
|
||||
!onGrab && !onDownload && !onRename && !onHealthIssue && !onApplicationUpdate && !onMovieDelete && !onMovieFileDelete ?
|
||||
<Label
|
||||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
@@ -190,6 +200,7 @@ Notification.propTypes = {
|
||||
onMovieFileDelete: PropTypes.bool.isRequired,
|
||||
onMovieFileDeleteForUpgrade: PropTypes.bool.isRequired,
|
||||
onHealthIssue: PropTypes.bool.isRequired,
|
||||
onApplicationUpdate: PropTypes.bool.isRequired,
|
||||
supportsOnGrab: PropTypes.bool.isRequired,
|
||||
supportsOnDownload: PropTypes.bool.isRequired,
|
||||
supportsOnMovieDelete: PropTypes.bool.isRequired,
|
||||
@@ -198,6 +209,7 @@ Notification.propTypes = {
|
||||
supportsOnUpgrade: PropTypes.bool.isRequired,
|
||||
supportsOnRename: PropTypes.bool.isRequired,
|
||||
supportsOnHealthIssue: PropTypes.bool.isRequired,
|
||||
supportsOnApplicationUpdate: PropTypes.bool.isRequired,
|
||||
onConfirmDeleteNotification: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ function NotificationEventItems(props) {
|
||||
onMovieFileDelete,
|
||||
onMovieFileDeleteForUpgrade,
|
||||
onHealthIssue,
|
||||
onApplicationUpdate,
|
||||
supportsOnGrab,
|
||||
supportsOnDownload,
|
||||
supportsOnUpgrade,
|
||||
@@ -30,6 +31,7 @@ function NotificationEventItems(props) {
|
||||
supportsOnMovieDelete,
|
||||
supportsOnMovieFileDelete,
|
||||
supportsOnMovieFileDeleteForUpgrade,
|
||||
supportsOnApplicationUpdate,
|
||||
supportsOnHealthIssue,
|
||||
includeHealthWarnings
|
||||
} = item;
|
||||
@@ -150,6 +152,17 @@ function NotificationEventItems(props) {
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onApplicationUpdate"
|
||||
helpText={translate('OnApplicationUpdateHelpText')}
|
||||
isDisabled={!supportsOnApplicationUpdate.value}
|
||||
{...onApplicationUpdate}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
|
||||
@@ -82,10 +82,18 @@ class DelayProfiles extends Component {
|
||||
>
|
||||
<div>
|
||||
<div className={styles.delayProfilesHeader}>
|
||||
<div className={styles.column}>Protocol</div>
|
||||
<div className={styles.column}>Usenet Delay</div>
|
||||
<div className={styles.column}>Torrent Delay</div>
|
||||
<div className={styles.tags}>Tags</div>
|
||||
<div className={styles.column}>
|
||||
{translate('Protocol')}
|
||||
</div>
|
||||
<div className={styles.column}>
|
||||
{translate('UsenetDelay')}
|
||||
</div>
|
||||
<div className={styles.column}>
|
||||
{translate('TorrentDelay')}
|
||||
</div>
|
||||
<div className={styles.tags}>
|
||||
{translate('Tags')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.delayProfiles}>
|
||||
|
||||
@@ -25,9 +25,15 @@ class QualityDefinitions extends Component {
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.quality}>Quality</div>
|
||||
<div className={styles.title}>Title</div>
|
||||
<div className={styles.sizeLimit}>Size Limit</div>
|
||||
<div className={styles.quality}>
|
||||
{translate('Quality')}
|
||||
</div>
|
||||
<div className={styles.title}>
|
||||
{translate('Title')}
|
||||
</div>
|
||||
<div className={styles.sizeLimit}>
|
||||
{translate('SizeLimit')}
|
||||
</div>
|
||||
|
||||
{
|
||||
advancedSettings ?
|
||||
|
||||
@@ -152,7 +152,7 @@ function TagDetailsModalContent(props) {
|
||||
|
||||
{
|
||||
indexers.length ?
|
||||
<FieldSet legend="Indexers">
|
||||
<FieldSet legend={translate('Indexers')}>
|
||||
{
|
||||
indexers.map((item) => {
|
||||
return (
|
||||
|
||||
@@ -14,7 +14,6 @@ function createRemoveItemHandler(section, url) {
|
||||
|
||||
const ajaxOptions = {
|
||||
url: `${url}/${id}?${$.param(queryParams, true)}`,
|
||||
dataType: 'text',
|
||||
method: 'DELETE'
|
||||
};
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ export default {
|
||||
selectedSchema.onMovieDelete = selectedSchema.supportsOnMovieDelete;
|
||||
selectedSchema.onMovieFileDelete = selectedSchema.supportsOnMovieFileDelete;
|
||||
selectedSchema.onMovieFileDeleteForUpgrade = selectedSchema.supportsOnMovieFileDeleteForUpgrade;
|
||||
selectedSchema.onApplicationUpdate = selectedSchema.supportsOnApplicationUpdate;
|
||||
|
||||
return selectedSchema;
|
||||
});
|
||||
|
||||
@@ -148,9 +148,10 @@ export const actionHandlers = handleThunks({
|
||||
return {
|
||||
id,
|
||||
path: item.path,
|
||||
movieId: item.movie.id,
|
||||
movieId: item.movie ? item.movie.id : undefined,
|
||||
quality: item.quality,
|
||||
languages: item.languages,
|
||||
releaseGroup: item.releaseGroup,
|
||||
downloadId: item.downloadId
|
||||
};
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ const paged = `${section}.paged`;
|
||||
|
||||
export const defaultState = {
|
||||
options: {
|
||||
includeUnknownMovieItems: false
|
||||
includeUnknownMovieItems: true
|
||||
},
|
||||
|
||||
status: {
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
var request = new HttpRequest($"https://expired.badssl.com");
|
||||
|
||||
Assert.Throws<HttpRequestException>(() => Subject.Execute(request));
|
||||
ExceptionVerification.ExpectedErrors(2);
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
private readonly Logger _logger;
|
||||
private readonly DateTime _startTime = DateTime.UtcNow;
|
||||
|
||||
public RuntimeInfo(IHostLifetime hostLifetime, Logger logger)
|
||||
public RuntimeInfo(Logger logger, IHostLifetime hostLifetime = null)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
|
||||
@@ -26,20 +26,17 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
private readonly ICertificateValidationService _certificateValidationService;
|
||||
private readonly IUserAgentBuilder _userAgentBuilder;
|
||||
private readonly ICached<System.Net.Http.HttpClient> _httpClientCache;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider,
|
||||
ICreateManagedWebProxy createManagedWebProxy,
|
||||
ICertificateValidationService certificateValidationService,
|
||||
IUserAgentBuilder userAgentBuilder,
|
||||
ICacheManager cacheManager,
|
||||
Logger logger)
|
||||
ICacheManager cacheManager)
|
||||
{
|
||||
_proxySettingsProvider = proxySettingsProvider;
|
||||
_createManagedWebProxy = createManagedWebProxy;
|
||||
_certificateValidationService = certificateValidationService;
|
||||
_userAgentBuilder = userAgentBuilder;
|
||||
_logger = logger;
|
||||
|
||||
_httpClientCache = cacheManager.GetCache<System.Net.Http.HttpClient>(typeof(ManagedHttpDispatcher));
|
||||
}
|
||||
@@ -79,49 +76,32 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
var httpClient = GetClient(request.Url);
|
||||
|
||||
HttpResponseMessage responseMessage;
|
||||
|
||||
try
|
||||
using var responseMessage = httpClient.Send(requestMessage, HttpCompletionOption.ResponseHeadersRead, cts.Token);
|
||||
{
|
||||
responseMessage = httpClient.Send(requestMessage, cts.Token);
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
_logger.Error(e, "HttpClient error");
|
||||
throw;
|
||||
}
|
||||
byte[] data = null;
|
||||
|
||||
byte[] data = null;
|
||||
|
||||
using (var responseStream = responseMessage.Content.ReadAsStream())
|
||||
{
|
||||
if (responseStream != null && responseStream != Stream.Null)
|
||||
try
|
||||
{
|
||||
try
|
||||
if (request.ResponseStream != null && responseMessage.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
if (request.ResponseStream != null && responseMessage.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
// A target ResponseStream was specified, write to that instead.
|
||||
// But only on the OK status code, since we don't want to write failures and redirects.
|
||||
responseStream.CopyTo(request.ResponseStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = responseStream.ToBytes();
|
||||
}
|
||||
responseMessage.Content.CopyTo(request.ResponseStream, null, cts.Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
throw new WebException("Failed to read complete http response", ex, WebExceptionStatus.ReceiveFailure, null);
|
||||
data = responseMessage.Content.ReadAsByteArrayAsync(cts.Token).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new WebException("Failed to read complete http response", ex, WebExceptionStatus.ReceiveFailure, null);
|
||||
}
|
||||
|
||||
var headers = responseMessage.Headers.ToNameValueCollection();
|
||||
|
||||
headers.Add(responseMessage.Content.Headers.ToNameValueCollection());
|
||||
|
||||
return new HttpResponse(request, new HttpHeader(headers), data, responseMessage.StatusCode);
|
||||
}
|
||||
|
||||
var headers = responseMessage.Headers.ToNameValueCollection();
|
||||
|
||||
headers.Add(responseMessage.Content.Headers.ToNameValueCollection());
|
||||
|
||||
return new HttpResponse(request, new HttpHeader(headers), data, responseMessage.StatusCode);
|
||||
}
|
||||
|
||||
protected virtual System.Net.Http.HttpClient GetClient(HttpUri uri)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class migrate_discord_from_slackFixture : MigrationTest<migrate_discord_from_slack>
|
||||
{
|
||||
private readonly JsonSerializerOptions _serializerSettings;
|
||||
|
||||
public migrate_discord_from_slackFixture()
|
||||
{
|
||||
_serializerSettings = new JsonSerializerOptions
|
||||
{
|
||||
AllowTrailingCommas = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
_serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_replace_old_url()
|
||||
{
|
||||
var webhookUrl = "https://discord.com/api/webhooks/922499153416847361/f9CAcD5i_E_-0AoPfMVa8igVK8h271HpJDbd6euUrPh9KonWlMCziLOSMmD-2SQ4CHmX/slack";
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Notifications").Row(new
|
||||
{
|
||||
Name = "SlackDiscord",
|
||||
Implementation = "Slack",
|
||||
Settings = new SlackNotificationSettings201
|
||||
{
|
||||
Icon = "TestURL",
|
||||
Username = "TestUsername",
|
||||
WebHookUrl = webhookUrl
|
||||
}.ToJson(),
|
||||
ConfigContract = "SlackSettings",
|
||||
OnGrab = true,
|
||||
OnDownload = true,
|
||||
OnUpgrade = true,
|
||||
OnRename = true,
|
||||
OnHealthIssue = true,
|
||||
OnMovieDelete = true,
|
||||
OnMovieFileDelete = true,
|
||||
OnMovieFileDeleteForUpgrade = true,
|
||||
IncludeHealthWarnings = true
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<NotificationEntity201>("SELECT Id,ConfigContract,Implementation,Name,Settings FROM Notifications");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
items.First().ConfigContract.Should().Be("DiscordSettings");
|
||||
var settings = JsonSerializer.Deserialize<DiscordNotificationSettings201>(items.First().Settings, _serializerSettings);
|
||||
settings.Avatar.Should().Be("TestURL");
|
||||
settings.Username.Should().Be("TestUsername");
|
||||
settings.WebHookUrl.Should().Be(webhookUrl.Replace("/slack", ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class remove_predbFixture : MigrationTest<remove_predb>
|
||||
{
|
||||
[Test]
|
||||
public void should_change_min_avail_from_predb_on_list()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("ImportLists").Row(new
|
||||
{
|
||||
Enabled = 1,
|
||||
EnableAuto = 1,
|
||||
RootFolderPath = "D:\\Movies",
|
||||
ProfileId = 1,
|
||||
MinimumAvailability = 4,
|
||||
ShouldMonitor = 1,
|
||||
Name = "IMDB List",
|
||||
Implementation = "RadarrLists",
|
||||
Settings = new RadarrListSettings169
|
||||
{
|
||||
APIURL = "https://api.radarr.video/v2",
|
||||
Path = "/imdb/list?listId=ls000199717",
|
||||
}.ToJson(),
|
||||
ConfigContract = "RadarrSettings"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<ListDefinition201>("SELECT Id, MinimumAvailability FROM ImportLists");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
items.First().MinimumAvailability.Should().Be(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_change_min_avail_from_predb_on_movie()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Movies").Row(new
|
||||
{
|
||||
Monitored = true,
|
||||
Title = "Title",
|
||||
CleanTitle = "CleanTitle",
|
||||
Status = 3,
|
||||
MinimumAvailability = 4,
|
||||
Images = new[] { new { CoverType = "Poster" } }.ToJson(),
|
||||
Recommendations = new[] { 1 }.ToJson(),
|
||||
HasPreDBEntry = false,
|
||||
Runtime = 90,
|
||||
OriginalLanguage = 1,
|
||||
ProfileId = 1,
|
||||
MovieFileId = 0,
|
||||
Path = string.Format("/Movies/{0}", "Title"),
|
||||
TitleSlug = 123456,
|
||||
TmdbId = 132456,
|
||||
Added = DateTime.UtcNow,
|
||||
LastInfoSync = DateTime.UtcNow,
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<Movie201>("SELECT Id, MinimumAvailability FROM Movies");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
items.First().MinimumAvailability.Should().Be(3);
|
||||
}
|
||||
}
|
||||
|
||||
public class ListDefinition201
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int MinimumAvailability { get; set; }
|
||||
}
|
||||
|
||||
public class Movie201
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int MinimumAvailability { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Data;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
@@ -38,6 +38,7 @@ namespace NzbDrone.Core.Test.Datastore.SqliteSchemaDumperTests
|
||||
result.Name.Should().Be(tableName);
|
||||
result.Columns.Count.Should().Be(1);
|
||||
result.Columns.First().Name.Should().Be(columnName);
|
||||
result.Columns.First().IsIdentity.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase(@"CREATE INDEX TestIndex ON TestTable (MyId)", "TestIndex", "TestTable", "MyId")]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Clients;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
@@ -67,6 +68,17 @@ namespace NzbDrone.Core.Test.Download
|
||||
return mock;
|
||||
}
|
||||
|
||||
private void WithTorrentIndexer(int downloadClientId)
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(v => v.Find(It.IsAny<int>()))
|
||||
.Returns(Builder<IndexerDefinition>
|
||||
.CreateNew()
|
||||
.With(v => v.Id = _nextId++)
|
||||
.With(v => v.DownloadClientId = downloadClientId)
|
||||
.Build());
|
||||
}
|
||||
|
||||
private void GivenBlockedClient(int id)
|
||||
{
|
||||
_blockedProviders.Add(new DownloadClientStatus
|
||||
@@ -223,5 +235,39 @@ namespace NzbDrone.Core.Test.Download
|
||||
client3.Definition.Id.Should().Be(2);
|
||||
client4.Definition.Id.Should().Be(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_always_choose_indexer_client()
|
||||
{
|
||||
WithUsenetClient();
|
||||
WithTorrentClient();
|
||||
WithTorrentClient();
|
||||
WithTorrentClient();
|
||||
WithTorrentIndexer(3);
|
||||
|
||||
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent, 1);
|
||||
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent, 1);
|
||||
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent, 1);
|
||||
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent, 1);
|
||||
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent, 1);
|
||||
|
||||
client1.Definition.Id.Should().Be(3);
|
||||
client2.Definition.Id.Should().Be(3);
|
||||
client3.Definition.Id.Should().Be(3);
|
||||
client4.Definition.Id.Should().Be(3);
|
||||
client5.Definition.Id.Should().Be(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fail_to_choose_client_when_indexer_reference_does_not_exist()
|
||||
{
|
||||
WithUsenetClient();
|
||||
WithTorrentClient();
|
||||
WithTorrentClient();
|
||||
WithTorrentClient();
|
||||
WithTorrentIndexer(5);
|
||||
|
||||
Assert.Throws<DownloadClientUnavailableException>(() => Subject.GetDownloadClient(DownloadProtocol.Torrent, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ namespace NzbDrone.Core.Test.Download
|
||||
.Returns(_downloadClients);
|
||||
|
||||
Mocker.GetMock<IProvideDownloadClient>()
|
||||
.Setup(v => v.GetDownloadClient(It.IsAny<DownloadProtocol>()))
|
||||
.Returns<DownloadProtocol>(v => _downloadClients.FirstOrDefault(d => d.Protocol == v));
|
||||
.Setup(v => v.GetDownloadClient(It.IsAny<DownloadProtocol>(), It.IsAny<int>()))
|
||||
.Returns<DownloadProtocol, int>((v, i) => _downloadClients.FirstOrDefault(d => d.Protocol == v));
|
||||
|
||||
var releaseInfo = Builder<ReleaseInfo>.CreateNew()
|
||||
.With(v => v.DownloadProtocol = DownloadProtocol.Usenet)
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.Framework
|
||||
Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>()));
|
||||
Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>()));
|
||||
Mocker.SetConstant<ICertificateValidationService>(new X509CertificateValidationService(Mocker.Resolve<ConfigService>(), TestLogger));
|
||||
Mocker.SetConstant<IHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<ICertificateValidationService>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<CacheManager>(), TestLogger));
|
||||
Mocker.SetConstant<IHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<ICertificateValidationService>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<CacheManager>()));
|
||||
Mocker.SetConstant<IHttpClient>(new HttpClient(Array.Empty<IHttpRequestInterceptor>(), Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), TestLogger));
|
||||
Mocker.SetConstant<IRadarrCloudRequestBuilder>(new RadarrCloudRequestBuilder());
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ namespace NzbDrone.Core.Test.HealthCheck
|
||||
|
||||
Mocker.SetConstant<IEnumerable<IProvideHealthCheck>>(new[] { _healthCheck });
|
||||
Mocker.SetConstant<ICacheManager>(Mocker.Resolve<CacheManager>());
|
||||
|
||||
Mocker.GetMock<IServerSideNotificationService>()
|
||||
.Setup(v => v.GetServerChecks())
|
||||
.Returns(new List<Core.HealthCheck.HealthCheck>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -204,5 +204,48 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
pageTier2.Url.Query.Should().NotContain("imdbid=0076759");
|
||||
pageTier2.Url.Query.Should().Contain("q=");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_encode_raw_title()
|
||||
{
|
||||
_capabilities.SupportedMovieSearchParameters = new[] { "q" };
|
||||
_capabilities.TextSearchEngine = "raw";
|
||||
|
||||
MovieSearchCriteria movieRawSearchCriteria = new MovieSearchCriteria
|
||||
{
|
||||
Movie = new Movies.Movie { Title = "Some Movie & Title: Words", Year = 2021, TmdbId = 123 },
|
||||
SceneTitles = new List<string> { "Some Movie & Title: Words" }
|
||||
};
|
||||
|
||||
var results = Subject.GetSearchRequests(movieRawSearchCriteria);
|
||||
|
||||
var page = results.GetTier(0).First().First();
|
||||
|
||||
page.Url.Query.Should().Contain("q=Some%20Movie%20%26%20Title%3A%20Words");
|
||||
page.Url.Query.Should().NotContain(" & ");
|
||||
page.Url.Query.Should().Contain("%26");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_clean_title_and_encode()
|
||||
{
|
||||
_capabilities.SupportedMovieSearchParameters = new[] { "q" };
|
||||
_capabilities.TextSearchEngine = "sphinx";
|
||||
|
||||
MovieSearchCriteria movieRawSearchCriteria = new MovieSearchCriteria
|
||||
{
|
||||
Movie = new Movies.Movie { Title = "Some Movie & Title: Words", Year = 2021, TmdbId = 123 },
|
||||
SceneTitles = new List<string> { "Some Movie & Title: Words" }
|
||||
};
|
||||
|
||||
var results = Subject.GetSearchRequests(movieRawSearchCriteria);
|
||||
|
||||
var page = results.GetTier(0).First().First();
|
||||
|
||||
page.Url.Query.Should().Contain("q=Some%20Movie%20and%20Title%20Words%202021");
|
||||
page.Url.Query.Should().Contain("and");
|
||||
page.Url.Query.Should().NotContain(" & ");
|
||||
page.Url.Query.Should().NotContain("%26");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,10 @@ namespace NzbDrone.Core.Test.Languages
|
||||
new object[] { 28, Language.Thai },
|
||||
new object[] { 29, Language.Bulgarian },
|
||||
new object[] { 30, Language.PortugueseBR },
|
||||
new object[] { 31, Language.Arabic }
|
||||
new object[] { 31, Language.Arabic },
|
||||
new object[] { 32, Language.Ukrainian },
|
||||
new object[] { 33, Language.Persian },
|
||||
new object[] { 34, Language.Bengali },
|
||||
};
|
||||
|
||||
public static object[] ToIntCases =
|
||||
@@ -81,7 +84,10 @@ namespace NzbDrone.Core.Test.Languages
|
||||
new object[] { Language.Thai, 28 },
|
||||
new object[] { Language.Bulgarian, 29 },
|
||||
new object[] { Language.PortugueseBR, 30 },
|
||||
new object[] { Language.Arabic, 31 }
|
||||
new object[] { Language.Arabic, 31 },
|
||||
new object[] { Language.Ukrainian, 32 },
|
||||
new object[] { Language.Persian, 33 },
|
||||
new object[] { Language.Bengali, 34 },
|
||||
};
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -20,6 +20,9 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
|
||||
[TestCase("wmv2, WMV2", "Droned.wmv", "WMV")]
|
||||
[TestCase("mpeg4, XVID", "", "XviD")]
|
||||
[TestCase("mpeg4, DIV3", "spsm.dvdrip.divx.avi'.", "DivX")]
|
||||
[TestCase("msmpeg4, DIV3", "Exit the Dragon, Enter the Tiger (1976) 360p MPEG Audio.avi", "DivX")]
|
||||
[TestCase("msmpeg4v2, DIV3", "Exit the Dragon, Enter the Tiger (1976) 360p MPEG Audio.avi", "DivX")]
|
||||
[TestCase("msmpeg4v3, DIV3", "Exit the Dragon, Enter the Tiger (1976) 360p MPEG Audio.avi", "DivX")]
|
||||
[TestCase("vp6, 4", "Top Gear - S12E01 - Lorries - SD TV.flv", "VP6")]
|
||||
[TestCase("vp7, VP70", "Sweet Seymour.avi", "VP7")]
|
||||
[TestCase("vp8, V_VP8", "Dick.mkv", "VP8")]
|
||||
|
||||
@@ -78,6 +78,11 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||
{
|
||||
TestLogger.Info("OnHealthIssue was called");
|
||||
}
|
||||
|
||||
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
|
||||
{
|
||||
TestLogger.Info("OnApplicationUpdate was called");
|
||||
}
|
||||
}
|
||||
|
||||
private class TestNotificationWithNoEvents : NotificationBase<TestSetting>
|
||||
@@ -116,6 +121,7 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||
notification.SupportsOnMovieFileDelete.Should().BeTrue();
|
||||
notification.SupportsOnMovieFileDeleteForUpgrade.Should().BeTrue();
|
||||
notification.SupportsOnHealthIssue.Should().BeTrue();
|
||||
notification.SupportsOnApplicationUpdate.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -131,6 +137,7 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||
notification.SupportsOnMovieFileDelete.Should().BeFalse();
|
||||
notification.SupportsOnMovieFileDeleteForUpgrade.Should().BeFalse();
|
||||
notification.SupportsOnHealthIssue.Should().BeFalse();
|
||||
notification.SupportsOnApplicationUpdate.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,15 +51,6 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
result.Languages.Should().Contain(Language.English);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.1982.Ger.Eng.AC3.DL.BDRip.x264-iNCEPTiON")]
|
||||
public void should_parse_language_english_german(string postTitle)
|
||||
{
|
||||
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
|
||||
|
||||
result.Languages.Should().Contain(Language.German);
|
||||
result.Languages.Should().Contain(Language.English);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.1994.Spanish.1080p.XviD-LOL")]
|
||||
[TestCase("Movie Title (2020)[BDRemux AVC 1080p][E-AC3 DD Plus 5.1 Castellano-Inglés Subs]")]
|
||||
[TestCase("Movie Title (2020) [UHDRemux2160p HDR][DTS-HD MA 5.1 AC3 5.1 Castellano - True-HD 7.1 Atmos Inglés Subs]")]
|
||||
@@ -72,6 +63,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.1994.German.1080p.XviD-LOL")]
|
||||
[TestCase("Movie.Title.2016.Ger.Dub.AAC.1080p.WebDL.x264-TKP21")]
|
||||
public void should_parse_language_german(string postTitle)
|
||||
{
|
||||
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
|
||||
@@ -161,6 +153,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
|
||||
[TestCase("Movie.Title.1994.Bulgarian.1080p.XviD-LOL")]
|
||||
[TestCase("Movie.Title.1994.BGAUDIO.1080p.XviD-LOL")]
|
||||
[TestCase("Movie.Title.1994.BG.AUDIO.1080p.XviD-LOL")]
|
||||
public void should_parse_language_bulgarian(string postTitle)
|
||||
{
|
||||
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
|
||||
@@ -314,6 +307,40 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
result.Languages.Should().BeEquivalentTo(Language.Arabic);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title [1989, BDRip] MVO + DVO + UKR (MVO) + Sub")]
|
||||
[TestCase("Movie.Title (2006) BDRemux 1080p 2xUkr | Sub Ukr")]
|
||||
[TestCase("Movie.Title [1984, BDRip 720p] MVO + MVO + Dub + AVO + 3xUkr")]
|
||||
[TestCase("Movie.Title.2019.UKRAINIAN.WEBRip.x264-VXT")]
|
||||
public void should_parse_language_ukrainian(string postTitle)
|
||||
{
|
||||
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
|
||||
|
||||
result.Languages.Should().BeEquivalentTo(Language.Ukrainian);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title [1937, BDRip 1080p] Dub UKR/Eng + Sub rus")]
|
||||
[TestCase("Movie.Title.[2003.BDRemux.1080p].Dub.MVO.(2xUkr/Fra).Sub.(Rus/Fra)")]
|
||||
public void should_parse_language_ukrainian_multi(string postTitle)
|
||||
{
|
||||
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
|
||||
|
||||
result.Languages.Should().Contain(Language.Ukrainian);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.2019.PERSIAN.WEBRip.x264-VXT")]
|
||||
public void should_parse_language_persian(string postTitle)
|
||||
{
|
||||
var result = Parser.Parser.ParseMovieTitle(postTitle);
|
||||
result.Languages.Should().BeEquivalentTo(Language.Persian);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.2019.BENGALI.WEBRip.x264-VXT")]
|
||||
public void should_parse_language_bengali(string postTitle)
|
||||
{
|
||||
var result = Parser.Parser.ParseMovieTitle(postTitle);
|
||||
result.Languages.Should().BeEquivalentTo(Language.Bengali);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.en.sub")]
|
||||
[TestCase("Movie Title.eng.sub")]
|
||||
[TestCase("Movie.Title.eng.forced.sub")]
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
* Superman.-.The.Man.of.Steel.1994-06.34.hybrid.DreamGirl-Novus-HD
|
||||
* Superman.-.The.Man.of.Steel.1994-05.33.hybrid.DreamGirl-Novus-HD
|
||||
* Constantine S1-E1-WEB-DL-1080p-NZBgeek
|
||||
* [TestCase("Valana la Movie FRENCH BluRay 720p 2016 kjhlj", "Valana la Movie")] Removed 2021-12-19 as this / the regex for this was breaking all movies w/ french in title
|
||||
*/
|
||||
|
||||
[Test]
|
||||
@@ -49,7 +50,6 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie.The.Final.Chapter.2016", "Movie The Final Chapter")]
|
||||
[TestCase("Der.Movie.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", "Der Movie James")]
|
||||
[TestCase("Movie.German.DL.AC3.Dubbed..BluRay.x264-PsO", "Movie")]
|
||||
[TestCase("Valana la Movie FRENCH BluRay 720p 2016 kjhlj", "Valana la Movie")]
|
||||
[TestCase("Valana la Movie TRUEFRENCH BluRay 720p 2016 kjhlj", "Valana la Movie")]
|
||||
[TestCase("Mission Movie: Rogue Movie (2015)<29>[XviD - Ita Ac3 - SoftSub Ita]azione, spionaggio, thriller *Prima Visione* Team mulnic Tom Cruise", "Mission Movie Rogue Movie")]
|
||||
[TestCase("Movie.Movie.2000.FRENCH..BluRay.-AiRLiNE", "Movie Movie")]
|
||||
@@ -61,6 +61,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("World.Movie.Z.2.EXTENDED.2013.German.DL.1080p.BluRay.AVC-XANOR", "World Movie Z 2")]
|
||||
[TestCase("G.I.Movie.Movie.2013.THEATRiCAL.COMPLETE.BLURAY-GLiMMER", "G.I. Movie Movie")]
|
||||
[TestCase("www.Torrenting.org - Movie.2008.720p.X264-DIMENSION", "Movie")]
|
||||
[TestCase("The.French.Movie.2013.720p.BluRay.x264 - ROUGH[PublicHD]", "The French Movie")]
|
||||
public void should_parse_movie_title(string postTitle, string title)
|
||||
{
|
||||
Parser.Parser.ParseMovieTitle(postTitle).PrimaryMovieTitle.Should().Be(title);
|
||||
@@ -199,6 +200,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
}
|
||||
|
||||
[TestCase("The.Italian.Movie.2025.720p.BluRay.X264-AMIABLE")]
|
||||
[TestCase("The.French.Movie.2013.720p.BluRay.x264 - ROUGH[PublicHD]")]
|
||||
public void should_not_parse_wrong_language_in_title(string postTitle)
|
||||
{
|
||||
var parsed = Parser.Parser.ParseMovieTitle(postTitle, true);
|
||||
|
||||
@@ -119,6 +119,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie.Name.S01E08.Tourmaline.Nepal.720p.HDTV.x264-DHD", false)]
|
||||
[TestCase("Movie.Name.US.S12E17.HR.WS.PDTV.X264-DIMENSION", false)]
|
||||
[TestCase("Movie.Name.The.Lost.Pilots.Movie.HR.WS.PDTV.x264-DHD", false)]
|
||||
[TestCase("Movie.Name.The.Lost.Pilots.Movie.HR.WS.PDTV.x264-DHD-Remux.mkv", false)]
|
||||
public void should_parse_hdtv720p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.TV, proper, Resolution.R720p);
|
||||
@@ -186,6 +187,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("[HorribleSubs] Movie Title! 2018 [Web][MKV][h264][1080p][AAC 2.0][Softsubs (HorribleSubs)]", false)]
|
||||
[TestCase("Movie.Title.2020.MULTi.1080p.WEB.H264-ALLDAYiN (S:285/L:11)", false)]
|
||||
[TestCase("Movie Title (2020) MULTi WEB 1080p x264-JiHEFF (S:317/L:28)", false)]
|
||||
[TestCase("Movie.Titles.2020.1080p.NF.WEB.DD2.0.x264-SNEAkY", false)]
|
||||
public void should_parse_webdl1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R1080p);
|
||||
@@ -256,6 +258,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie Name 2005 1080p UHD BluRay DD+7.1 x264-LoRD.mkv", false)]
|
||||
[TestCase("Movie.Name.2011.1080p.UHD.BluRay.DD5.1.HDR.x265-CtrlHD.mkv", false)]
|
||||
[TestCase("Movie.Name.2016.German.DTS.DL.1080p.UHDBD.x265-TDO.mkv", false)]
|
||||
[TestCase("Movie.Name.2021.1080p.BDLight.x265-AVCDVD", false)]
|
||||
public void should_parse_bluray1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R1080p);
|
||||
@@ -272,6 +275,8 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie.Title.2019.2160p.MBLURAY.x264-MBLURAYFANS.mkv", false)]
|
||||
[TestCase("Movie.Title.2017.2160p.MBluRay.x264-TREBLE.mkv", false)]
|
||||
[TestCase("Movie.Name.2020.German.UHDBD.2160p.HDR10.HEVC.EAC3.DL-pmHD.mkv", false)]
|
||||
[TestCase("Movie.Title.2014.2160p.UHD.BluRay.X265-IAMABLE.mkv", false)]
|
||||
[TestCase("Movie.Title.2014.2160p.BDRip.AAC.7.1.HDR10.x265.10bit-Markll", false)]
|
||||
public void should_parse_bluray2160p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R2160p);
|
||||
@@ -425,6 +430,8 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie.Title.2016.1080p.KORSUBS.WEBRip.x264.AAC2.0-RADARR", "KORSUBS")]
|
||||
[TestCase("Movie Title 2017 HC 720p HDRiP DD5 1 x264-LEGi0N", "Generic Hardcoded Subs")]
|
||||
[TestCase("Movie.Title.2017.720p.SUBBED.HDRip.V2.XViD-26k.avi", "Generic Hardcoded Subs")]
|
||||
[TestCase("Movie Title! 2018 [Web][MKV][h264][480p][AAC 2.0][Softsubs]", null)]
|
||||
[TestCase("Movie Title! 2019 [HorribleSubs][Web][MKV][h264][848x480][AAC 2.0][Softsubs(HorribleSubs)]", null)]
|
||||
public void should_parse_hardcoded_subs(string postTitle, string sub)
|
||||
{
|
||||
QualityParser.ParseQuality(postTitle).HardcodedSubs.Should().Be(sub);
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace NzbDrone.Core.Test.UpdateTests
|
||||
[Test]
|
||||
public void finds_update_when_version_lower()
|
||||
{
|
||||
NotBsd();
|
||||
UseRealHttp();
|
||||
Subject.GetLatestUpdate("develop", new Version(3, 0)).Should().NotBeNull();
|
||||
}
|
||||
@@ -43,8 +42,6 @@ namespace NzbDrone.Core.Test.UpdateTests
|
||||
[Test]
|
||||
public void should_get_recent_updates()
|
||||
{
|
||||
NotBsd();
|
||||
|
||||
const string branch = "nightly";
|
||||
UseRealHttp();
|
||||
var recent = Subject.GetRecentUpdates(branch, new Version(3, 0), null);
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace NzbDrone.Core.Datastore
|
||||
IEnumerable<TModel> All();
|
||||
int Count();
|
||||
TModel Get(int id);
|
||||
TModel Find(int id);
|
||||
TModel Insert(TModel model);
|
||||
TModel Update(TModel model);
|
||||
TModel Upsert(TModel model);
|
||||
@@ -99,6 +100,13 @@ namespace NzbDrone.Core.Datastore
|
||||
return model;
|
||||
}
|
||||
|
||||
public TModel Find(int id)
|
||||
{
|
||||
var model = Query(c => c.Id == id).SingleOrDefault();
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
public IEnumerable<TModel> Get(IEnumerable<int> ids)
|
||||
{
|
||||
if (!ids.Any())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using FluentMigrator;
|
||||
using FluentMigrator;
|
||||
|
||||
//using FluentMigrator.Expressions;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
@@ -13,12 +13,12 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
if (!Schema.Schema("dbo").Table("NetImport").Column("MinimumAvailability").Exists())
|
||||
{
|
||||
Alter.Table("NetImport").AddColumn("MinimumAvailability").AsInt32().WithDefaultValue(MovieStatusType.PreDB);
|
||||
Alter.Table("NetImport").AddColumn("MinimumAvailability").AsInt32().WithDefaultValue(MovieStatusType.Released);
|
||||
}
|
||||
|
||||
if (!Schema.Schema("dbo").Table("Movies").Column("MinimumAvailability").Exists())
|
||||
{
|
||||
Alter.Table("Movies").AddColumn("MinimumAvailability").AsInt32().WithDefaultValue(MovieStatusType.PreDB);
|
||||
Alter.Table("Movies").AddColumn("MinimumAvailability").AsInt32().WithDefaultValue(MovieStatusType.Released);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||
|
||||
//Manual SQL, Fluent Migrator doesn't support multi-column unique contraint on table creation, SQLite doesn't support adding it after creation
|
||||
Execute.Sql("CREATE TABLE MovieTranslations(" +
|
||||
"Id INTEGER PRIMARY KEY, " +
|
||||
"Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
|
||||
"MovieId INTEGER NOT NULL, " +
|
||||
"Title TEXT, " +
|
||||
"CleanTitle TEXT, " +
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Dapper;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(201)]
|
||||
public class migrate_discord_from_slack : NzbDroneMigrationBase
|
||||
{
|
||||
private readonly JsonSerializerOptions _serializerSettings;
|
||||
|
||||
public migrate_discord_from_slack()
|
||||
{
|
||||
_serializerSettings = new JsonSerializerOptions
|
||||
{
|
||||
AllowTrailingCommas = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
_serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true));
|
||||
}
|
||||
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(MigrateDiscordFromSlack);
|
||||
}
|
||||
|
||||
private void MigrateDiscordFromSlack(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var notificationRows = conn.Query<NotificationEntity201>($"SELECT Id,ConfigContract,Implementation,Name,Settings FROM Notifications WHERE Implementation = 'Slack'");
|
||||
|
||||
var discordSlackNotifications = notificationRows.Where(n => JsonSerializer.Deserialize<SlackNotificationSettings201>(n.Settings, _serializerSettings).WebHookUrl.Contains("discord"));
|
||||
|
||||
if (!discordSlackNotifications.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (NotificationEntity201 notification in discordSlackNotifications)
|
||||
{
|
||||
SlackNotificationSettings201 settings = JsonSerializer.Deserialize<SlackNotificationSettings201>(notification.Settings, _serializerSettings);
|
||||
DiscordNotificationSettings201 discordSettings = new DiscordNotificationSettings201
|
||||
{
|
||||
Avatar = settings.Icon,
|
||||
GrabFields = new List<int> { 0, 1, 2, 3, 5, 6, 7, 8, 9 },
|
||||
ImportFields = new List<int> { 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12 },
|
||||
Username = settings.Username,
|
||||
WebHookUrl = settings.WebHookUrl.Replace("/slack", "")
|
||||
};
|
||||
|
||||
notification.ConfigContract = "DiscordSettings";
|
||||
notification.Implementation = "Discord";
|
||||
notification.Name = $"{notification.Name}-Slack_Migrated";
|
||||
notification.Settings = JsonSerializer.Serialize(discordSettings, _serializerSettings);
|
||||
}
|
||||
|
||||
var updateSql = "UPDATE Notifications SET ConfigContract = @ConfigContract, " +
|
||||
"Implementation = @Implementation, " +
|
||||
"Name = @Name, " +
|
||||
"Settings = @Settings " +
|
||||
"WHERE Id = @Id";
|
||||
|
||||
conn.Execute(updateSql, discordSlackNotifications, transaction: tran);
|
||||
}
|
||||
}
|
||||
|
||||
public class NotificationEntity201
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string ConfigContract { get; set; }
|
||||
public string Implementation { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Settings { get; set; }
|
||||
}
|
||||
|
||||
public class SlackNotificationSettings201
|
||||
{
|
||||
public string Channel { get; set; }
|
||||
public string Icon { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string WebHookUrl { get; set; }
|
||||
}
|
||||
|
||||
public class DiscordNotificationSettings201
|
||||
{
|
||||
public string Avatar { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string WebHookUrl { get; set; }
|
||||
public IEnumerable<int> GrabFields { get; set; }
|
||||
public IEnumerable<int> ImportFields { get; set; }
|
||||
}
|
||||
}
|
||||
23
src/NzbDrone.Core/Datastore/Migration/202_remove_predb.cs
Normal file
23
src/NzbDrone.Core/Datastore/Migration/202_remove_predb.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(202)]
|
||||
public class remove_predb : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
//Set PreDb entries to Released
|
||||
Update.Table("Movies").Set(new { MinimumAvailability = 3 }).Where(new { MinimumAvailability = 4 });
|
||||
Update.Table("ImportLists").Set(new { MinimumAvailability = 3 }).Where(new { MinimumAvailability = 4 });
|
||||
|
||||
//Should never be set, but just in case
|
||||
Update.Table("Movies").Set(new { Status = 3 }).Where(new { Status = 4 });
|
||||
Update.Table("ImportListMovies").Set(new { Status = 3 }).Where(new { Status = 4 });
|
||||
|
||||
//Remove unused column
|
||||
Delete.Column("HasPreDBEntry").FromTable("Movies");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(203)]
|
||||
public class add_on_update_to_notifications : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Notifications").AddColumn("OnApplicationUpdate").AsBoolean().WithDefaultValue(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(204)]
|
||||
public class ensure_identity_on_id_columns : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
//Purge Commands before reworking tables
|
||||
Delete.FromTable("Commands").AllRows();
|
||||
|
||||
Alter.Column("Id").OnTable("Movies").AsInt32().PrimaryKey().Identity();
|
||||
Alter.Column("Id").OnTable("MovieTranslations").AsInt32().PrimaryKey().Identity();
|
||||
Alter.Column("Id").OnTable("Commands").AsInt32().PrimaryKey().Identity();
|
||||
Alter.Column("Id").OnTable("Credits").AsInt32().PrimaryKey().Identity();
|
||||
Alter.Column("Id").OnTable("Profiles").AsInt32().PrimaryKey().Identity();
|
||||
Alter.Column("Id").OnTable("PendingReleases").AsInt32().PrimaryKey().Identity();
|
||||
Alter.Column("Id").OnTable("NamingConfig").AsInt32().PrimaryKey().Identity();
|
||||
Alter.Column("Id").OnTable("History").AsInt32().PrimaryKey().Identity();
|
||||
Alter.Column("Id").OnTable("Blocklist").AsInt32().PrimaryKey().Identity();
|
||||
Alter.Column("Id").OnTable("MovieFiles").AsInt32().PrimaryKey().Identity();
|
||||
Alter.Column("Id").OnTable("CustomFormats").AsInt32().PrimaryKey().Identity();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(205)]
|
||||
public class download_client_per_indexer : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Indexers").AddColumn("DownloadClientId").AsInt32().WithDefaultValue(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using FluentMigrator.Model;
|
||||
using FluentMigrator.Runner.Processors.SQLite;
|
||||
|
||||
@@ -66,6 +67,24 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
|
||||
if (columnReader.Read() == SqliteSyntaxReader.TokenType.StringToken)
|
||||
{
|
||||
if (columnReader.ValueToUpper == "PRIMARY")
|
||||
{
|
||||
columnReader.SkipTillToken(SqliteSyntaxReader.TokenType.ListStart);
|
||||
if (columnReader.Read() == SqliteSyntaxReader.TokenType.Identifier)
|
||||
{
|
||||
var pk = table.Columns.First(v => v.Name == columnReader.Value);
|
||||
pk.IsPrimaryKey = true;
|
||||
pk.IsNullable = true;
|
||||
pk.IsUnique = true;
|
||||
if (columnReader.Buffer.ToUpperInvariant().Contains("AUTOINCREMENT"))
|
||||
{
|
||||
pk.IsIdentity = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (columnReader.ValueToUpper == "CONSTRAINT" ||
|
||||
columnReader.ValueToUpper == "PRIMARY" || columnReader.ValueToUpper == "UNIQUE" ||
|
||||
columnReader.ValueToUpper == "CHECK" || columnReader.ValueToUpper == "FOREIGN")
|
||||
|
||||
@@ -89,7 +89,8 @@ namespace NzbDrone.Core.Datastore
|
||||
.Ignore(i => i.SupportsOnMovieDelete)
|
||||
.Ignore(i => i.SupportsOnMovieFileDelete)
|
||||
.Ignore(i => i.SupportsOnMovieFileDeleteForUpgrade)
|
||||
.Ignore(i => i.SupportsOnHealthIssue);
|
||||
.Ignore(i => i.SupportsOnHealthIssue)
|
||||
.Ignore(i => i.SupportsOnApplicationUpdate);
|
||||
|
||||
Mapper.Entity<MetadataDefinition>("Metadata").RegisterModel()
|
||||
.Ignore(x => x.ImplementationName)
|
||||
|
||||
@@ -265,12 +265,12 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
case WebExceptionStatus.ConnectionClosed:
|
||||
return new NzbDroneValidationFailure("UseSsl", "Verify SSL settings")
|
||||
{
|
||||
DetailedDescription = "Please verify your SSL configuration on both Deluge and NzbDrone."
|
||||
DetailedDescription = "Please verify your SSL configuration on both Deluge and Radarr."
|
||||
};
|
||||
case WebExceptionStatus.SecureChannelFailure:
|
||||
return new NzbDroneValidationFailure("UseSsl", "Unable to connect through SSL")
|
||||
{
|
||||
DetailedDescription = "Drone is unable to connect to Deluge using SSL. This problem could be computer related. Please try to configure both drone and Deluge to not use SSL."
|
||||
DetailedDescription = "Drone is unable to connect to Deluge using SSL. This problem could be computer related. Please try to configure both Radarr and Deluge to not use SSL."
|
||||
};
|
||||
default:
|
||||
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
|
||||
|
||||
@@ -118,14 +118,17 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
try
|
||||
{
|
||||
var serialNumber = _serialNumberProvider.GetSerialNumber(Settings);
|
||||
var sharedFolder = GetDownloadDirectory() ?? GetDefaultDir();
|
||||
var outputPath = new OsPath($"/{sharedFolder.TrimStart('/')}");
|
||||
var path = _sharedFolderResolver.RemapToFullPath(outputPath, Settings, serialNumber);
|
||||
|
||||
// Download station returns the path without the leading `/`, but the leading
|
||||
// slash is required to get the full path back from download station.
|
||||
var path = new OsPath($"/{GetDownloadDirectory()}");
|
||||
|
||||
var fullPath = _sharedFolderResolver.RemapToFullPath(path, Settings, serialNumber);
|
||||
|
||||
return new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, path) }
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, fullPath) }
|
||||
};
|
||||
}
|
||||
catch (DownloadClientException e)
|
||||
|
||||
@@ -142,14 +142,17 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
try
|
||||
{
|
||||
var serialNumber = _serialNumberProvider.GetSerialNumber(Settings);
|
||||
var sharedFolder = GetDownloadDirectory() ?? GetDefaultDir();
|
||||
var outputPath = new OsPath($"/{sharedFolder.TrimStart('/')}");
|
||||
var path = _sharedFolderResolver.RemapToFullPath(outputPath, Settings, serialNumber);
|
||||
|
||||
// Download station returns the path without the leading `/`, but the leading
|
||||
// slash is required to get the full path back from download station.
|
||||
var path = new OsPath($"/{GetDownloadDirectory()}");
|
||||
|
||||
var fullPath = _sharedFolderResolver.RemapToFullPath(path, Settings, serialNumber);
|
||||
|
||||
return new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, path) }
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, fullPath) }
|
||||
};
|
||||
}
|
||||
catch (DownloadClientException e)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Core.Download.Clients;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
public interface IProvideDownloadClient
|
||||
{
|
||||
IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol);
|
||||
IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol, int indexerId = 0);
|
||||
IEnumerable<IDownloadClient> GetDownloadClients();
|
||||
IDownloadClient Get(int id);
|
||||
}
|
||||
@@ -18,17 +19,23 @@ namespace NzbDrone.Core.Download
|
||||
private readonly Logger _logger;
|
||||
private readonly IDownloadClientFactory _downloadClientFactory;
|
||||
private readonly IDownloadClientStatusService _downloadClientStatusService;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
private readonly ICached<int> _lastUsedDownloadClient;
|
||||
|
||||
public DownloadClientProvider(IDownloadClientStatusService downloadClientStatusService, IDownloadClientFactory downloadClientFactory, ICacheManager cacheManager, Logger logger)
|
||||
public DownloadClientProvider(IDownloadClientStatusService downloadClientStatusService,
|
||||
IDownloadClientFactory downloadClientFactory,
|
||||
IIndexerFactory indexerFactory,
|
||||
ICacheManager cacheManager,
|
||||
Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_downloadClientFactory = downloadClientFactory;
|
||||
_downloadClientStatusService = downloadClientStatusService;
|
||||
_indexerFactory = indexerFactory;
|
||||
_lastUsedDownloadClient = cacheManager.GetCache<int>(GetType(), "lastDownloadClientId");
|
||||
}
|
||||
|
||||
public IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol)
|
||||
public IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol, int indexerId = 0)
|
||||
{
|
||||
var availableProviders = _downloadClientFactory.GetAvailableProviders().Where(v => v.Protocol == downloadProtocol).ToList();
|
||||
|
||||
@@ -37,6 +44,18 @@ namespace NzbDrone.Core.Download
|
||||
return null;
|
||||
}
|
||||
|
||||
if (indexerId > 0)
|
||||
{
|
||||
var indexer = _indexerFactory.Find(indexerId);
|
||||
|
||||
if (indexer != null && indexer.DownloadClientId > 0)
|
||||
{
|
||||
var client = availableProviders.SingleOrDefault(d => d.Definition.Id == indexer.DownloadClientId);
|
||||
|
||||
return client ?? throw new DownloadClientUnavailableException($"Indexer specified download client is not available");
|
||||
}
|
||||
}
|
||||
|
||||
var blockedProviders = new HashSet<int>(_downloadClientStatusService.GetBlockedProviders().Select(v => v.ProviderId));
|
||||
|
||||
if (blockedProviders.Any())
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace NzbDrone.Core.Download
|
||||
Ensure.That(remoteMovie.Movie, () => remoteMovie.Movie).IsNotNull();
|
||||
|
||||
var downloadTitle = remoteMovie.Release.Title;
|
||||
var downloadClient = _downloadClientProvider.GetDownloadClient(remoteMovie.Release.DownloadProtocol);
|
||||
var downloadClient = _downloadClientProvider.GetDownloadClient(remoteMovie.Release.DownloadProtocol, remoteMovie.Release.IndexerId);
|
||||
|
||||
if (downloadClient == null)
|
||||
{
|
||||
|
||||
39
src/NzbDrone.Core/HealthCheck/Checks/SlackUrlCheck.cs
Normal file
39
src/NzbDrone.Core/HealthCheck/Checks/SlackUrlCheck.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Notifications;
|
||||
using NzbDrone.Core.Notifications.Slack;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
[CheckOn(typeof(ProviderUpdatedEvent<INotificationFactory>))]
|
||||
[CheckOn(typeof(ProviderDeletedEvent<INotificationFactory>))]
|
||||
[CheckOn(typeof(ProviderStatusChangedEvent<INotificationFactory>))]
|
||||
public class SlackUrlCheck : HealthCheckBase
|
||||
{
|
||||
private readonly INotificationFactory _notificationFactory;
|
||||
|
||||
public SlackUrlCheck(INotificationFactory notificationFactory, ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
{
|
||||
_notificationFactory = notificationFactory;
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var discordSlackNotifications = _notificationFactory.GetAvailableProviders().Where(n => n.ConfigContract.Equals("SlackSettings") && (n.Definition.Settings as SlackSettings).WebHookUrl.Contains("discord"));
|
||||
|
||||
if (discordSlackNotifications.Empty())
|
||||
{
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
string.Format(_localizationService.GetLocalizedString("DiscordUrlInSlackNotification"),
|
||||
string.Join(", ", discordSlackNotifications.Select(n => n.Name))),
|
||||
"#discord-as-slack-notification");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ namespace NzbDrone.Core.HealthCheck
|
||||
private readonly IProvideHealthCheck[] _startupHealthChecks;
|
||||
private readonly IProvideHealthCheck[] _scheduledHealthChecks;
|
||||
private readonly Dictionary<Type, IEventDrivenHealthCheck[]> _eventDrivenHealthChecks;
|
||||
private readonly IServerSideNotificationService _serverSideNotificationService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly Logger _logger;
|
||||
@@ -32,11 +33,13 @@ namespace NzbDrone.Core.HealthCheck
|
||||
private readonly ICached<HealthCheck> _healthCheckResults;
|
||||
|
||||
public HealthCheckService(IEnumerable<IProvideHealthCheck> healthChecks,
|
||||
IServerSideNotificationService serverSideNotificationService,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheManager cacheManager,
|
||||
Logger logger)
|
||||
{
|
||||
_healthChecks = healthChecks.ToArray();
|
||||
_serverSideNotificationService = serverSideNotificationService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_cacheManager = cacheManager;
|
||||
_logger = logger;
|
||||
@@ -72,6 +75,8 @@ namespace NzbDrone.Core.HealthCheck
|
||||
var results = healthChecks.Select(c => c.Check())
|
||||
.ToList();
|
||||
|
||||
results.AddRange(_serverSideNotificationService.GetServerChecks());
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (result.Type == HealthCheckResult.Ok)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cloud;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck
|
||||
{
|
||||
public interface IServerSideNotificationService
|
||||
{
|
||||
public List<HealthCheck> GetServerChecks();
|
||||
}
|
||||
|
||||
public class ServerSideNotificationService : IServerSideNotificationService
|
||||
{
|
||||
private readonly IHttpClient _client;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ServerSideNotificationService(IHttpClient client, IConfigFileProvider configFileProvider, IRadarrCloudRequestBuilder cloudRequestBuilder, Logger logger)
|
||||
{
|
||||
_client = client;
|
||||
_configFileProvider = configFileProvider;
|
||||
_cloudRequestBuilder = cloudRequestBuilder.Services;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<HealthCheck> GetServerChecks()
|
||||
{
|
||||
var request = _cloudRequestBuilder.Create()
|
||||
.Resource("/notification")
|
||||
.AddQueryParam("version", BuildInfo.Version)
|
||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
||||
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("branch", _configFileProvider.Branch)
|
||||
.Build();
|
||||
try
|
||||
{
|
||||
_logger.Trace("Getting server side health notifications");
|
||||
var response = _client.Execute(request);
|
||||
var result = Json.Deserialize<List<ServerNotificationResponse>>(response.Content);
|
||||
return result.Select(x => new HealthCheck(GetType(), x.Type, x.Message, x.WikiUrl)).ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to retrieve server notifications");
|
||||
return new List<HealthCheck>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ServerNotificationResponse
|
||||
{
|
||||
public HealthCheckResult Type { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string WikiUrl { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
@@ -7,6 +7,7 @@ namespace NzbDrone.Core.Indexers
|
||||
public bool EnableRss { get; set; }
|
||||
public bool EnableAutomaticSearch { get; set; }
|
||||
public bool EnableInteractiveSearch { get; set; }
|
||||
public int DownloadClientId { get; set; }
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
public bool SupportsRss { get; set; }
|
||||
public bool SupportsSearch { get; set; }
|
||||
|
||||
@@ -204,7 +204,8 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
|
||||
private static string NewsnabifyTitle(string title)
|
||||
{
|
||||
return title.Replace("+", "%20");
|
||||
var newtitle = title.Replace("+", " ");
|
||||
return Uri.EscapeDataString(newtitle);
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace NzbDrone.Core.Indexers.Nyaa
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
foreach (var queryTitle in searchCriteria.CleanSceneTitles)
|
||||
foreach (var queryTitle in searchCriteria.SceneTitles)
|
||||
{
|
||||
pageableRequests.Add(GetPagedRequests(MaxPages, PrepareQuery(string.Format("{0} {1}", queryTitle, searchCriteria.Movie.Year))));
|
||||
}
|
||||
|
||||
@@ -102,6 +102,9 @@ namespace NzbDrone.Core.Languages
|
||||
public static Language Bulgarian => new Language(29, "Bulgarian");
|
||||
public static Language PortugueseBR => new Language(30, "Portuguese (Brazil)");
|
||||
public static Language Arabic => new Language(31, "Arabic");
|
||||
public static Language Ukrainian => new Language(32, "Unkrainian");
|
||||
public static Language Persian => new Language(33, "Persian");
|
||||
public static Language Bengali => new Language(34, "Bengali");
|
||||
public static Language Any => new Language(-1, "Any");
|
||||
public static Language Original => new Language(-2, "Original");
|
||||
|
||||
@@ -143,6 +146,9 @@ namespace NzbDrone.Core.Languages
|
||||
Bulgarian,
|
||||
PortugueseBR,
|
||||
Arabic,
|
||||
Ukrainian,
|
||||
Persian,
|
||||
Bengali,
|
||||
Any,
|
||||
Original
|
||||
};
|
||||
|
||||
@@ -1017,7 +1017,7 @@
|
||||
"Agenda": "جدول أعمال",
|
||||
"Age": "عمر",
|
||||
"AfterManualRefresh": "بعد التحديث اليدوي",
|
||||
"AddToDownloadQueue": "تمت الإضافة إلى قائمة انتظار التنزيل",
|
||||
"AddToDownloadQueue": "إضافة إلى قائمة انتظار التنزيل",
|
||||
"AddRootFolder": "إضافة مجلد جذر",
|
||||
"AddRestriction": "إضافة القيد",
|
||||
"AddRemotePathMapping": "إضافة تعيين المسار البعيد",
|
||||
@@ -1047,7 +1047,7 @@
|
||||
"AddIndexer": "أضف مفهرس",
|
||||
"AddImportExclusionHelpText": "منع إضافة الفيلم إلى Radarr بواسطة القوائم",
|
||||
"AddExclusion": "أضف استثناء",
|
||||
"AddedToDownloadQueue": "تمت الإضافة إلى قائمة الانتظار التي تم تنزيلها",
|
||||
"AddedToDownloadQueue": "تمت الإضافة إلى قائمة انتظار التنزيلات",
|
||||
"Added": "مضاف",
|
||||
"AddDownloadClient": "إضافة وكيل التحميل",
|
||||
"AddDelayProfile": "إضافة ملف تعريف تأخير",
|
||||
|
||||
1
src/NzbDrone.Core/Localization/Core/bn.json
Normal file
1
src/NzbDrone.Core/Localization/Core/bn.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -529,7 +529,7 @@
|
||||
"ShowTitleHelpText": "Filmtitel unter dem Plakat anzeigen",
|
||||
"ShowUnknownMovieItems": "Unzugeordente Filmeinträge anzeigen",
|
||||
"SkipFreeSpaceCheck": "Pürfung des freien Speichers überspringen",
|
||||
"SkipFreeSpaceCheckWhenImportingHelpText": "Aktiviere dies wenn es nicht möglich ist, den freien Speicherplatz vom Stammverzeichnis zu ermitteln",
|
||||
"SkipFreeSpaceCheckWhenImportingHelpText": "Aktiviere dies, wenn es nicht möglich ist, den freien Speicherplatz vom Stammverzeichnis zu ermitteln",
|
||||
"SorryThatMovieCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.",
|
||||
"SourcePath": "Quellpfad",
|
||||
"SourceRelativePath": "Relativer Quellpfad",
|
||||
@@ -544,7 +544,7 @@
|
||||
"TestAllIndexers": "Alle testen",
|
||||
"TestAllLists": "Alle testen",
|
||||
"TimeFormat": "Zeitformat",
|
||||
"UpdateMechanismHelpText": "Benutze den Built-In Updater oder ein Script",
|
||||
"UpdateMechanismHelpText": "Benutze den Build-In Updater oder ein Script",
|
||||
"UpdateScriptPathHelpText": "Pfad zu einem benutzerdefinierten Skript, das ein extrahiertes Update-Paket übernimmt und den Rest des Update-Prozesses abwickelt",
|
||||
"UpgradeAllowedHelpText": "Wenn deaktiviert wird die Qualität nicht verbessert",
|
||||
"Uptime": "Laufzeit",
|
||||
@@ -954,7 +954,7 @@
|
||||
"Months": "Monate",
|
||||
"MonitoredStatus": "Beobachtet/Status",
|
||||
"Monday": "Montag",
|
||||
"MissingFromDisk": "Radarr konnte die Datei nicht auf der Festplatte finden, deshalb wurde es gelöscht",
|
||||
"MissingFromDisk": "Radarr konnte die Datei nicht auf der Festplatte finden, daher wurde die Datei aus der Datenbank entfernt",
|
||||
"Minutes": "Minuten",
|
||||
"MinimumCustomFormatScore": "Minimum der eigenen Formate Bewertungspunkte",
|
||||
"Min": "Min.",
|
||||
@@ -1037,7 +1037,7 @@
|
||||
"AllMoviesInPathHaveBeenImported": "Alle Filme in {0} wurden importiert",
|
||||
"AllFiles": "Alle Dateien",
|
||||
"AfterManualRefresh": "Nach manuellen aktualisieren",
|
||||
"AddToDownloadQueue": "Zur Downloadwarteschlange hinzugefügt",
|
||||
"AddToDownloadQueue": "Zur Downloadwarteschlange hinzufügen",
|
||||
"AddRootFolder": "Stammordner hinzufügen",
|
||||
"AddQualityProfile": "Qualitätsprofil hinzufügen",
|
||||
"AddNotification": "Benachrichtigung hinzufügen",
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
"AllMoviesInPathHaveBeenImported": "All movies in {0} have been imported",
|
||||
"AllowHardcodedSubs": "Allow Hardcoded Subs",
|
||||
"AllowHardcodedSubsHelpText": "Detected hardcoded subs will be automatically downloaded",
|
||||
"AllowMovieChangeClickToChangeMovie": "Click to change movie",
|
||||
"AllResultsHiddenFilter": "All results are hidden by the applied filter",
|
||||
"AlreadyInYourLibrary": "Already in your library",
|
||||
"AlternativeTitle": "Alternative Title",
|
||||
@@ -46,8 +45,8 @@
|
||||
"AnalyseVideoFiles": "Analyze video files",
|
||||
"Analytics": "Analytics",
|
||||
"AnalyticsEnabledHelpText": "Send anonymous usage and error information to Radarr's servers. This includes information on your browser, which Radarr WebUI pages you use, error reporting as well as OS and runtime version. We will use this information to prioritize features and bug fixes.",
|
||||
"AnnoucedMsg": "Movie is announced",
|
||||
"Announced": "Announced",
|
||||
"AnnouncedMsg": "Movie is announced",
|
||||
"ApiKey": "API Key",
|
||||
"AppDataDirectory": "AppData directory",
|
||||
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
|
||||
@@ -128,6 +127,7 @@
|
||||
"ClickToChangeLanguage": "Click to change language",
|
||||
"ClickToChangeMovie": "Click to change movie",
|
||||
"ClickToChangeQuality": "Click to change quality",
|
||||
"ClickToChangeReleaseGroup": "Click to change release group",
|
||||
"ClientPriority": "Client Priority",
|
||||
"CloneCustomFormat": "Clone Custom Format",
|
||||
"CloneFormatTag": "Clone Format Tag",
|
||||
@@ -226,10 +226,10 @@
|
||||
"DetailedProgressBar": "Detailed Progress Bar",
|
||||
"DetailedProgressBarHelpText": "Show text on progress bar",
|
||||
"Details": "Details",
|
||||
"Digital": "Digital",
|
||||
"DigitalRelease": "Digital Release",
|
||||
"Disabled": "Disabled",
|
||||
"Discord": "Discord",
|
||||
"DiscordUrlInSlackNotification": "You have a Discord notification setup as a Slack notification. Set this up as a Discord notification for better functionality. The effected notifications are: {0}",
|
||||
"Discover": "Discover",
|
||||
"DiskSpace": "Disk Space",
|
||||
"Docker": "Docker",
|
||||
@@ -238,8 +238,6 @@
|
||||
"DoneEditingGroups": "Done Editing Groups",
|
||||
"DoNotPrefer": "Do Not Prefer",
|
||||
"DoNotUpgradeAutomatically": "Do not Upgrade Automatically",
|
||||
"DotNetVersionCheckNotRecommendedMessage": "Currently installed .Net Framework {0} is supported but we recommend upgrading to at least {1}.",
|
||||
"DotNetVersionCheckOldUnsupportedMessage": "Currently installed .Net Framework {0} is old and unsupported. Please upgrade the .Net Framework to at least {1}.",
|
||||
"Download": "Download",
|
||||
"DownloadClient": "Download Client",
|
||||
"DownloadClientCheckDownloadingToRoot": "Download client {0} places downloads in the root folder {1}. You should not download to a root folder.",
|
||||
@@ -303,7 +301,6 @@
|
||||
"ErrorRestoringBackup": "Error restoring backup",
|
||||
"Events": "Events",
|
||||
"EventType": "Event Type",
|
||||
"Example": "Example",
|
||||
"Exception": "Exception",
|
||||
"Excluded": "Excluded",
|
||||
"ExcludeMovie": "Exclude Movie",
|
||||
@@ -328,11 +325,11 @@
|
||||
"FileNames": "File Names",
|
||||
"FileNameTokens": "File Name Tokens",
|
||||
"Files": "Files",
|
||||
"Filesize": "Filesize",
|
||||
"FileWasDeletedByUpgrade": "File was deleted to import an upgrade",
|
||||
"FileWasDeletedByViaUI": "File was deleted via the UI",
|
||||
"Filter": "Filter",
|
||||
"FilterPlaceHolder": "Search movies",
|
||||
"Filters": "Filters",
|
||||
"FirstDayOfWeek": "First Day of Week",
|
||||
"Fixed": "Fixed",
|
||||
"FocusSearchBox": "Focus Search Box",
|
||||
@@ -420,6 +417,7 @@
|
||||
"IncludeUnknownMovieItemsHelpText": "Show items without a movie in the queue. This could include removed movies or anything else in Radarr's category",
|
||||
"IncludeUnmonitored": "Include Unmonitored",
|
||||
"Indexer": "Indexer",
|
||||
"IndexerDownloadClientHelpText": "Specify which download client is used for grabs from this indexer",
|
||||
"IndexerFlags": "Indexer Flags",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "All indexers are unavailable due to failures for more than 6 hours",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indexers unavailable due to failures for more than 6 hours: {0}",
|
||||
@@ -472,9 +470,9 @@
|
||||
"LoadingMovieExtraFilesFailed": "Loading movie extra files failed",
|
||||
"LoadingMovieFilesFailed": "Loading movie files failed",
|
||||
"Local": "Local",
|
||||
"LocalPath": "Local Path",
|
||||
"Location": "Location",
|
||||
"LogFiles": "Log Files",
|
||||
"LogFilesLocationMessage": "Log files are located in:",
|
||||
"Logging": "Logging",
|
||||
"LogLevel": "Log Level",
|
||||
"LogLevelTraceHelpTextWarning": "Trace logging should only be enabled temporarily",
|
||||
@@ -489,6 +487,7 @@
|
||||
"ManualImportSelectLanguage": "Manual Import - Select Language",
|
||||
"ManualImportSelectMovie": "Manual Import - Select Movie",
|
||||
"ManualImportSelectQuality": " Manual Import - Select Quality",
|
||||
"ManualImportSetReleaseGroup": "Manual Import - Set Release Group",
|
||||
"MappedDrivesRunningAsService": "Mapped network drives are not available when running as a Windows Service. Please see the FAQ for more information",
|
||||
"MarkAsFailed": "Mark as Failed",
|
||||
"MarkAsFailedMessageText": "Are you sure you want to mark '{0}' as failed?",
|
||||
@@ -499,7 +498,6 @@
|
||||
"MaximumSizeHelpText": "Maximum size for a release to be grabbed in MB. Set to zero to set to unlimited",
|
||||
"Mechanism": "Mechanism",
|
||||
"MediaInfo": "Media Info",
|
||||
"MediaInfoDllCheckMessage": "MediaInfo Library could not be loaded {0}",
|
||||
"MediaManagement": "Media Management",
|
||||
"MediaManagementSettings": "Media Management Settings",
|
||||
"MediaManagementSettingsSummary": "Naming and file management settings",
|
||||
@@ -525,7 +523,7 @@
|
||||
"MinutesNinety": "90 Minutes: {0}",
|
||||
"MinutesSixty": "60 Minutes: {0}",
|
||||
"Missing": "Missing",
|
||||
"MissingFromDisk": "Radarr was unable to find the file on disk so it was removed",
|
||||
"MissingFromDisk": "Radarr was unable to find the file on disk so the file was unlinked from the movie in the database",
|
||||
"MissingMonitoredAndConsideredAvailable": "Missing (Monitored)",
|
||||
"MissingNotMonitored": "Missing (Unmonitored)",
|
||||
"Mode": "Mode",
|
||||
@@ -536,14 +534,6 @@
|
||||
"MonitoredOnly": "Monitored Only",
|
||||
"MonitoredStatus": "Monitored/Status",
|
||||
"MonitorMovie": "Monitor Movie",
|
||||
"MonoBSDSupportCheckMessage": "The operating system {0} will not be supported in the next release of Radarr.",
|
||||
"MonoNotNetCoreCheckMessage": "Please upgrade to the .NET Core version of Radarr, Mono versions will not be supported in the next release.",
|
||||
"MonoTlsCheckMessage": "Radarr Mono 4.x tls workaround still enabled, consider removing MONO_TLS_PROVIDER=legacy environment option",
|
||||
"MonoVersion": "Mono Version",
|
||||
"MonoVersionCheckNotSupportedMessage": "Currently installed Mono version {0} is no longer supported. Please upgrade Mono to version {1}.",
|
||||
"MonoVersionCheckOldNotSupportedMessage": "Currently installed Mono version {0} is old and unsupported. Please upgrade Mono to version {1}.",
|
||||
"MonoVersionCheckUpgradeRecommendedMessage": "Currently installed Mono version {0} is supported but upgrading to {1} is recommended.",
|
||||
"Monox86SupportCheckMessage": "The current operating system is 32 bit, this will not be supported in the next release of Radarr.",
|
||||
"Month": "Month",
|
||||
"Months": "Months",
|
||||
"More": "More",
|
||||
@@ -556,7 +546,6 @@
|
||||
"MoveFolders2": "Would you like to move the movie files from '{0}' to '{1}' ?",
|
||||
"Movie": "Movie",
|
||||
"MovieAlreadyExcluded": "Movie already Excluded",
|
||||
"MovieAvailableButMissing": "Movie Available, but Missing",
|
||||
"MovieChat": "Movie Chat",
|
||||
"MovieDetailsNextMovie": "Movie Details: Next Movie",
|
||||
"MovieDetailsPreviousMovie": "Movie Details: Previous Movie",
|
||||
@@ -565,7 +554,6 @@
|
||||
"MovieFiles": "Movie Files",
|
||||
"MovieFilesTotaling": "Movie Files Totaling",
|
||||
"MovieFolderFormat": "Movie Folder Format",
|
||||
"MovieHasntReleasedYet": "Movie hasn't released yet",
|
||||
"MovieID": "Movie ID",
|
||||
"MovieIndex": "Movie Index",
|
||||
"MovieIndexScrollBottom": "Movie Index: Scroll Bottom",
|
||||
@@ -585,7 +573,6 @@
|
||||
"MoviesSelectedInterp": "{0} Movie(s) Selected",
|
||||
"MovieTitle": "Movie Title",
|
||||
"MovieTitleHelpText": "The title of the movie to exclude (can be anything meaningful)",
|
||||
"MovieWasDownloadedAndSorted": "Movie was downloaded and sorted",
|
||||
"MovieYear": "Movie Year",
|
||||
"MovieYearHelpText": "The year of the movie to exclude",
|
||||
"MultiLanguage": "Multi-Language",
|
||||
@@ -604,7 +591,6 @@
|
||||
"NoBackupsAreAvailable": "No backups are available",
|
||||
"NoChange": "No Change",
|
||||
"NoChanges": "No Changes",
|
||||
"NoCinemaRelease": "No Cinema Release",
|
||||
"NoEventsFound": "No events found",
|
||||
"NoHistory": "No history",
|
||||
"NoLeaveIt": "No, Leave It",
|
||||
@@ -627,6 +613,8 @@
|
||||
"NoVideoFilesFoundSelectedFolder": "No video files were found in the selected folder",
|
||||
"OAuthPopupMessage": "Pop-ups are being blocked by your browser",
|
||||
"Ok": "Ok",
|
||||
"OnApplicationUpdate": "On Application Update",
|
||||
"OnApplicationUpdateHelpText": "On Application Update",
|
||||
"OnDownloadHelpText": "On Import",
|
||||
"OnGrab": "On Grab",
|
||||
"OnGrabHelpText": "On Grab",
|
||||
@@ -727,7 +715,6 @@
|
||||
"Queued": "Queued",
|
||||
"QueueIsEmpty": "Queue is empty",
|
||||
"QuickImport": "Move Automatically",
|
||||
"Radarr": "Radarr",
|
||||
"RadarrCalendarFeed": "Radarr Calendar Feed",
|
||||
"RadarrSupportsAnyDownloadClient": "Radarr supports many popular torrent and usenet download clients.",
|
||||
"RadarrSupportsAnyIndexer": "Radarr supports any indexer that uses the Newznab standard, as well as other indexers listed below.",
|
||||
@@ -758,7 +745,6 @@
|
||||
"RelativePath": "Relative Path",
|
||||
"ReleaseBranchCheckOfficialBranchMessage": "Branch {0} is not a valid Radarr release branch, you will not receive updates",
|
||||
"Released": "Released",
|
||||
"ReleaseDate": "Release Date",
|
||||
"ReleaseDates": "Release Dates",
|
||||
"ReleasedMsg": "Movie is released",
|
||||
"ReleaseGroup": "Release Group",
|
||||
@@ -767,8 +753,8 @@
|
||||
"ReleaseTitle": "Release Title",
|
||||
"ReleaseWillBeProcessedInterp": "Release will be processed {0}",
|
||||
"Reload": "Reload",
|
||||
"RemotePath": "Remote Path",
|
||||
"RemotePathMappingCheckBadDockerPath": "You are using docker; download client {0} places downloads in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.",
|
||||
"RemotePathMappingCheckDockerFilesMissing": "You are using docker; download client {0} reported files in {1} but this directory does not appear to exist inside the container. Review your remote path mappings and container volume settings.",
|
||||
"RemotePathMappingCheckDockerFolderMissing": "You are using docker; download client {0} places downloads in {1} but this directory does not appear to exist inside the container. Review your remote path mappings and container volume settings.",
|
||||
"RemotePathMappingCheckDownloadPermissions": "Radarr can see but not access downloaded movie {0}. Likely permissions error.",
|
||||
"RemotePathMappingCheckFileRemoved": "File {0} was removed part way through processing.",
|
||||
@@ -874,13 +860,15 @@
|
||||
"SelectDotDot": "Select...",
|
||||
"SelectFolder": "Select Folder",
|
||||
"SelectLanguage": "Select Language",
|
||||
"SelectLanguges": "Select Languages",
|
||||
"SelectLanguages": "Select Languages",
|
||||
"SelectMovie": "Select Movie",
|
||||
"SelectQuality": "Select Quality",
|
||||
"SelectReleaseGroup": "Select Release Group",
|
||||
"SendAnonymousUsageData": "Send Anonymous Usage Data",
|
||||
"SetPermissions": "Set Permissions",
|
||||
"SetPermissionsLinuxHelpText": "Should chmod be run when files are imported/renamed?",
|
||||
"SetPermissionsLinuxHelpTextWarning": "If you're unsure what these settings do, do not alter them.",
|
||||
"SetReleaseGroup": "Set Release Group",
|
||||
"SetTags": "Set Tags",
|
||||
"Settings": "Settings",
|
||||
"SettingsEnableColorImpairedMode": "Enable Color-Impaired Mode",
|
||||
@@ -929,6 +917,7 @@
|
||||
"ShowYear": "Show Year",
|
||||
"Shutdown": "Shutdown",
|
||||
"Size": "Size",
|
||||
"SizeLimit": "Size Limit",
|
||||
"SizeOnDisk": "Size on Disk",
|
||||
"SkipFreeSpaceCheck": "Skip Free Space Check",
|
||||
"SkipFreeSpaceCheckWhenImportingHelpText": "Use when Radarr is unable to detect free space from your movie root folder",
|
||||
@@ -1112,4 +1101,4 @@
|
||||
"YesMoveFiles": "Yes, Move the Files",
|
||||
"Yesterday": "Yesterday",
|
||||
"YouCanAlsoSearch": "You can also search using TMDb ID or IMDb ID of a movie. e.g. `tmdb:71663`"
|
||||
}
|
||||
}
|
||||
1
src/NzbDrone.Core/Localization/Core/fa.json
Normal file
1
src/NzbDrone.Core/Localization/Core/fa.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -163,7 +163,7 @@
|
||||
"AddNotification": "Lisää ilmoitus",
|
||||
"AddQualityProfile": "Lisää laatuprofiili",
|
||||
"AddRootFolder": "Lisää juurikansio",
|
||||
"AddToDownloadQueue": "Lisätty latausjonoon",
|
||||
"AddToDownloadQueue": "Lisää latausjonoon",
|
||||
"AfterManualRefresh": "Manuaalisen päivityksen jälkeen",
|
||||
"AllFiles": "Kaikki tiedostot",
|
||||
"AllMoviesInPathHaveBeenImported": "Kaikki kansion '{0}' elokuvat on tuotu",
|
||||
@@ -222,7 +222,7 @@
|
||||
"MinimumFreeSpace": "Vapaan tallennustilan vähimmäismäärä",
|
||||
"MinimumLimits": "Vähimmäismäärät",
|
||||
"Minutes": "Minuuttia",
|
||||
"MissingFromDisk": "Radarr ei löytänyt tiedostoa levyltä, joten se poistettiin",
|
||||
"MissingFromDisk": "Radarr ei löytänyt tiedostoa levyltä, joten sen kytkös kirjaston elokuvaan poistettiin",
|
||||
"Monday": "maanantai",
|
||||
"MonoVersion": "Mono-versio",
|
||||
"MonoVersionCheckNotSupportedMessage": "Tällä hetkellä asennettua mono-versiota {0} ei enää tueta. Päivitä Mono versioon {1}.",
|
||||
@@ -883,12 +883,12 @@
|
||||
"ShowDateAdded": "Näytä lisäyspäivä",
|
||||
"ShowMonitored": "Näytä valvonta",
|
||||
"ShowPath": "Näytä sijainti",
|
||||
"ShowMonitoredHelpText": "Näytä elokuvan valvontatila julisteen alla.",
|
||||
"ShowMonitoredHelpText": "Näytä valvontatila julisteen alla.",
|
||||
"ShowMovieInformation": "Näytä elokuvan tiedot",
|
||||
"ShowMovieInformationHelpText": "Näytä elokuvien lajutyypit ja ikäluokitukset.",
|
||||
"ShownClickToHide": "Näkyvissä, piilota painamalla",
|
||||
"ShowQualityProfile": "Näytä laatuprofiili",
|
||||
"ShowQualityProfileHelpText": "Näytä elokuvan laatuprofiili julisteen alla.",
|
||||
"ShowQualityProfileHelpText": "Näytä laatuprofiili julisteen alla.",
|
||||
"ShowRatings": "Näytä arviot",
|
||||
"ShowSearch": "Näytä haku",
|
||||
"ShowSearchHelpText": "Näytä hakupainike osoitettaessa.",
|
||||
@@ -1111,5 +1111,8 @@
|
||||
"BypassDelayIfHighestQuality": "Ohita, jos korkein laatu",
|
||||
"RemoveFailed": "Poisto epäonnistui",
|
||||
"RemoveCompleted": "Poista valmistuneet",
|
||||
"RemoveDownloadsAlert": "Poistoasetukset on siirretty yllä olevassa taulukossa yksittäisten lataustyökalujen alle."
|
||||
"RemoveDownloadsAlert": "Poistoasetukset on siirretty yllä olevassa taulukossa yksittäisten lataustyökalujen alle.",
|
||||
"OnApplicationUpdate": "Kun sovellus päivittyy",
|
||||
"OnApplicationUpdateHelpText": "Kun sovellus päivittyy",
|
||||
"DiscordUrlInSlackNotification": "Olet määrittänyt Discord-ilmoituksen Slack-ilmoitukseksi. Määritä se Discord-ilmoitukseksi parempaa toiminnallisuutta varten. Koskee seuraavia ilmoituksia: {0}"
|
||||
}
|
||||
|
||||
@@ -1094,5 +1094,7 @@
|
||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Êtes-vous sûr de vouloir désinstaller {0} objet{1} de la file d'attente ?",
|
||||
"BlocklistReleases": "Mettre cette release sur la liste noire",
|
||||
"RemotePathMappingCheckGenericPermissions": "Le client de téléchargement {0} met les téléchargements dans {1} mais Radarr ne peut voir ce répertoire. Il est possible que vous ayez besoin d'ajuster les permissions de ce dossier.",
|
||||
"RemotePathMappingCheckLocalWrongOSPath": "Le client de téléchargement {0} met les téléchargements dans {1} mais il ne s'agit pas d'un chemin {2} valide. Vérifiez les paramètres de votre client de téléchargement."
|
||||
"RemotePathMappingCheckLocalWrongOSPath": "Le client de téléchargement {0} met les téléchargements dans {1} mais il ne s'agit pas d'un chemin {2} valide. Vérifiez les paramètres de votre client de téléchargement.",
|
||||
"RemotePathMappingCheckFilesGenericPermissions": "Le client de téléchargement {0} met les téléchargements dans {1} mais Radarr ne peut voir ce répertoire. Il est possible que vous ayez besoin d'ajuster les permissions de ce dossier.",
|
||||
"RemotePathMappingCheckFilesLocalWrongOSPath": "Le client de téléchargement {0} met les téléchargements dans {1} mais il ne s'agit pas d'un chemin {2} valide. Vérifiez les paramètres de votre client de téléchargement."
|
||||
}
|
||||
|
||||
@@ -893,11 +893,11 @@
|
||||
"AllMoviesInPathHaveBeenImported": "Az összes film a(z) {0} -ból importálva lett",
|
||||
"AllFiles": "Összes fájl",
|
||||
"AfterManualRefresh": "A kézi frissítés után",
|
||||
"AddToDownloadQueue": "Hozzáadva a letöltési sorhoz",
|
||||
"AddToDownloadQueue": "Hozzáadás a letöltési sorhoz",
|
||||
"AddRootFolder": "Gyökérmappa hozzáadása",
|
||||
"AddQualityProfile": "Minőségi profil hozzáadása",
|
||||
"AddNotification": "Értesítés hozzáadása",
|
||||
"AddedToDownloadQueue": "Hozzáadva a letöltött elemekhez",
|
||||
"AddedToDownloadQueue": "Hozzáadva a letöltési sorhoz",
|
||||
"AddDownloadClient": "Letöltőkliens hozzáadása",
|
||||
"AddDelayProfile": "Késleltetési profil hozzáadása",
|
||||
"AddCustomFormat": "Egyéni formátum hozzáadása",
|
||||
@@ -1028,7 +1028,7 @@
|
||||
"Months": "Hónapok",
|
||||
"MonitoredStatus": "Megfigyelt/Státusz",
|
||||
"Monday": "Hétfő",
|
||||
"MissingFromDisk": "A Radarr nem találta a megadott fájlt a lemezen, így eltávolitotta azt",
|
||||
"MissingFromDisk": "A Radarr nem találta a fájlt a lemezen, ezért a fájlt eltávolítottuk az adatbázisból",
|
||||
"Minutes": "percek",
|
||||
"MinimumCustomFormatScore": "Minimum Egyéni Formátum száma",
|
||||
"Min": "minimum",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"RootFolderCheckSingleMessage": "Cartella radice mancante: {0}",
|
||||
"Wanted": "Ricercato",
|
||||
"View": "Guarda",
|
||||
"View": "Vista",
|
||||
"UpdateSelected": "Aggiorna i Film Selezionati",
|
||||
"UnselectAll": "Deseleziona Tutto",
|
||||
"Unmonitored": "Non Seguito",
|
||||
@@ -59,7 +59,7 @@
|
||||
"Clear": "Cancella",
|
||||
"Certification": "Certificazione",
|
||||
"AppDataLocationHealthCheckMessage": "Non è possibile effettuare l'aggiornamento per evitare di cancellare AppData",
|
||||
"Analytics": "Statistiche",
|
||||
"Analytics": "Analisi",
|
||||
"Added": "Aggiunto",
|
||||
"About": "Versione",
|
||||
"Year": "Anno",
|
||||
@@ -259,7 +259,7 @@
|
||||
"AnalyticsEnabledHelpText": "Inviare informazioni anonime sull'utilizzo e sugli errori ai server di Radarr. Ciò include informazioni del tuo browser, come le pagine di Radarr che utilizzi, la segnalazione di errori e la versione del sistema operativo e del runtime. Utilizzeremo queste informazioni per dare priorità alle funzioni e alle correzioni di bug.",
|
||||
"AnalyseVideoFiles": "Analizza i file video",
|
||||
"AlternativeTitle": "Titolo Alternativo",
|
||||
"AlreadyInYourLibrary": "Già presente nella libreria",
|
||||
"AlreadyInYourLibrary": "Già presente nella tua libreria",
|
||||
"AllowHardcodedSubsHelpText": "Se verranno rilevati sottotitoli impressi il film verrà comunque scaricato",
|
||||
"AllowHardcodedSubs": "Permetti i sottotitoli hardcoded (impressi direttamente nel file)",
|
||||
"Warn": "Attenzione",
|
||||
@@ -602,7 +602,7 @@
|
||||
"GeneralSettings": "Impostazioni Generali",
|
||||
"ForMoreInformationOnTheIndividualIndexers": "Per maggiori informazioni sui singoli Indexer clicca sul pulsante info.",
|
||||
"ForMoreInformationOnTheIndividualImportListsClinkOnTheInfoButtons": "Per maggiori informazioni sulle singole liste di importazione clicca sul pulsante info.",
|
||||
"ForMoreInformationOnTheIndividualDownloadClients": "Per maggiori informazioni sui singoli client di download clicca sul pulsante info.",
|
||||
"ForMoreInformationOnTheIndividualDownloadClients": "Per maggiori informazioni sui singoli download del client clicca sul pulsante info.",
|
||||
"FollowPerson": "Segui Persona",
|
||||
"Folders": "Cartelle",
|
||||
"Fixed": "Fissato",
|
||||
@@ -734,7 +734,7 @@
|
||||
"TagsHelpText": "Si applica ai film con almeno un tag corrispondente",
|
||||
"TagIsNotUsedAndCanBeDeleted": "Il tag non è usato e può essere eliminato",
|
||||
"TagCannotBeDeletedWhileInUse": "Non può essere cancellato mentre è in uso",
|
||||
"Table": "Tavola",
|
||||
"Table": "Tabella",
|
||||
"SuggestTranslationChange": "Richiedi cambio di traduzione",
|
||||
"SubfolderWillBeCreatedAutomaticallyInterp": "La sottocartella '{0}' verrà creata automaticamente",
|
||||
"StartupDirectory": "Cartella di avvio",
|
||||
@@ -857,11 +857,11 @@
|
||||
"AllMoviesInPathHaveBeenImported": "Tutti i Film in {0} sono stati importati",
|
||||
"AllFiles": "Tutti i file",
|
||||
"AfterManualRefresh": "Dopo il refresh manuale",
|
||||
"AddToDownloadQueue": "Aggiunto alla coda di download",
|
||||
"AddToDownloadQueue": "Aggiungi alla coda di download",
|
||||
"AddRootFolder": "Aggiungi cartella Root",
|
||||
"AddQualityProfile": "Aggiungi profilo qualità",
|
||||
"AddNotification": "Aggiungi notifica",
|
||||
"AddedToDownloadQueue": "Aggiungi alla coda di download",
|
||||
"AddedToDownloadQueue": "Aggiunto alla coda di download",
|
||||
"AddDownloadClient": "Aggiungi client di download",
|
||||
"AddCustomFormat": "Aggiungi formato personalizzato",
|
||||
"Add": "Aggiungi",
|
||||
@@ -1079,5 +1079,31 @@
|
||||
"Blocklisted": "Lista Nera",
|
||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Sei sicuro di voler cancellare {0} elemento{1} dalla coda?",
|
||||
"BlocklistReleases": "Release in blacklist",
|
||||
"BypassDelayIfHighestQuality": "Non considerare se la qualità è massima"
|
||||
"BypassDelayIfHighestQuality": "Non considerare se la qualità è massima",
|
||||
"ImportListMissingRoot": "Persa la cartella principale per l’importazione delle liste : {0}",
|
||||
"ImportListMultipleMissingRoots": "Diverse cartelle principale sono perse per l’importazione: {0}",
|
||||
"From": "Da",
|
||||
"MonoBSDSupportCheckMessage": "Il sistema operativo {0} non sarà supportato nella prossima release di Radarr.",
|
||||
"RemotePathMappingCheckDockerFilesMissing": "Stai utilizzando docker; il download client {0} riporta files in {1} ma questa directory non sembra esistere nel contenitore. Controlla la mappa dei percorsi remoti e le impostazioni dei volumi del container.",
|
||||
"IndexerTagHelpText": "Usa questo indicizzatore per i film con almeno un tag corrispondente. Lascia in bianco per usarlo con tutti i film.",
|
||||
"RemotePathMappingCheckFilesBadDockerPath": "Stai utilizzando docker; Il client di download {0} riporta files in {1} ma questo non è un percorso valido {2}. Controlla la mappa dei percorsi remoti e le impostazioni del client di download.",
|
||||
"RemotePathMappingCheckFilesGenericPermissions": "Il download client {0} riporta files in {1} ma Radarr non può vedere questa directory. Potrebbe essere necessario aggiustare i permessi della cartella.",
|
||||
"OnApplicationUpdate": "All'aggiornamento dell'applicazione",
|
||||
"OnApplicationUpdateHelpText": "All'aggiornamento dell'applicazione",
|
||||
"RemotePathMappingCheckBadDockerPath": "Stai utilizzando docker; Il client di download {0} mette i download in {1} ma questo non è un percorso valido {2}. Controlla la mappa dei percorsi remoti e le impostazioni del client di download.",
|
||||
"RemotePathMappingCheckDockerFolderMissing": "Stai utilizzando docker; il download client {0} riporta files in {1} ma questa directory non sembra esistere nel contenitore. Controlla la mappa dei percorsi remoti e le impostazioni dei volumi del container.",
|
||||
"RemotePathMappingCheckDownloadPermissions": "Radarr può vedere ma non accedere al film scaricato {0}. Probabilmente per un errore nei permessi.",
|
||||
"RemotePathMappingCheckFolderPermissions": "Radarr può vedere ma non accedere alla directory {0}. Probabilmente per un errore nei permessi.",
|
||||
"RemoveCompleted": "Rimuovi completati",
|
||||
"RemoveDownloadsAlert": "Le impostazioni per la rimozione sono stati spostati nelle impostazioni individuali dei Client di Download nella tabella sopra.",
|
||||
"RemoveFailed": "Rimozione fallita",
|
||||
"RemotePathMappingCheckGenericPermissions": "Il download client {0} mette i files in {1} ma Radarr non può vedere questa directory. Potrebbe essere necessario aggiustare i permessi della cartella.",
|
||||
"RemotePathMappingCheckImportFailed": "Radarr ha fallito nell'importare un film. Controlla i logs per dettagli.",
|
||||
"RemoveSelectedItem": "Rimuovi elemento selezionato",
|
||||
"RemoveSelectedItems": "Rimuovi elementi selezionati",
|
||||
"TaskUserAgentTooltip": "User-Agent esposto dalla app che ha chiamato la API",
|
||||
"UpdateAvailable": "É disponibile un nuovo aggiornamento",
|
||||
"BypassDelayIfHighestQualityHelpText": "Salta il ritardo quando la release ha la più alta qualità abilitata nel profilo qualità con il protocollo preferito",
|
||||
"Monox86SupportCheckMessage": "Il sistema operativo attuale è a 32bit, questo non sarà supportato nella prossima release di Radarr.",
|
||||
"NotificationTriggersHelpText": "Selezionare quali eventi attiveranno questa notifica"
|
||||
}
|
||||
|
||||
@@ -484,7 +484,7 @@
|
||||
"Links": "Koppelingen",
|
||||
"IconForCutoffUnmet": "Icoon voor Onbereikte Drempel",
|
||||
"EnableMediaInfoHelpText": "Video informatie extraheren zoals resolutie, speelduur en codec informatie van bestanden. Dit maakt het noodzakelijk dat Radarr delen van een bestand moet inlezen dewelke hoge schijf- of netwerkactiviteit kan veroorzaken tijdens het scannen.",
|
||||
"BindAddress": "Aanhaak Adres",
|
||||
"BindAddress": "Gebonden Adres",
|
||||
"PreferIndexerFlagsHelpText": "Verkies uitgaves met speciale flags",
|
||||
"MinimumAgeHelpText": "Enkel Usenet: Minimale leeftijd in minuten voor NZB bestanden alvorens ze worden opgehaald. Gebruik dit om nieuwe uitgaves de tijd te geven om tot bij uw Usenet provider toe te komen.",
|
||||
"LanguageHelpText": "Taal voor Uitgaves",
|
||||
@@ -1096,12 +1096,12 @@
|
||||
"RemotePathMappingCheckLocalWrongOSPath": "Lokale downloadclient {0} plaatst downloads in {1}, maar dit is geen geldig {2}-pad. Controleer de instellingen van uw downloadclient.",
|
||||
"TaskUserAgentTooltip": "User-Agent geleverd door de app die de API heeft aangeroepen",
|
||||
"AreYouSureYouWantToRemoveTheSelectedItemsFromBlocklist": "Ben je zeker dat je de geselecteerde items wil verwijderen van de uitzonderingslijst?",
|
||||
"Blocklist": "Zwarte lijst",
|
||||
"Blocklist": "Blokkeerlijst",
|
||||
"BlocklistHelpText": "Voorkom dat Radarr deze release nogmaals automatisch ophaalt",
|
||||
"BlocklistRelease": "Uitgave op Zwarte lijst zetten",
|
||||
"BlocklistRelease": "Uitgave op blokkeerlijst zetten",
|
||||
"RemoveFromBlocklist": "Verwijder van zwarte lijst",
|
||||
"UnableToLoadBlocklist": "Kon zwarte lijst niet laden",
|
||||
"Blocklisted": "Zwarte lijst",
|
||||
"Blocklisted": "Blokkeerlijst",
|
||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Ben je zeker dat je {1} item{2} uit de wachtrij wilt verwijderen?",
|
||||
"BlocklistReleases": "Uitgave op Zwarte lijst zetten"
|
||||
"BlocklistReleases": "Uitgave op blokkeerlijst zetten"
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@
|
||||
"IgnoredHelpText": "A versão será rejeitada caso contenha um ou mais destes termos (sem distinção de maiúsculas ou minúsculas)",
|
||||
"IgnoreDeletedMovies": "Deixar de monitorar filmes eliminados",
|
||||
"IgnoredAddresses": "Endereços ignorados",
|
||||
"IconForCutoffUnmet": "Ícone para Limite não-correspondido",
|
||||
"IconForCutoffUnmet": "Ícone para Limite não correspondido",
|
||||
"ICalHttpUrlHelpText": "Copie este URL para seu(s) cliente(s) ou clique para subscrever-se caso seu browser suporte webcal",
|
||||
"ICalFeed": "Feed do iCal",
|
||||
"Hostname": "Nome do anfitrião",
|
||||
@@ -440,8 +440,8 @@
|
||||
"EnableInteractiveSearch": "Ativar pesquisa interativa",
|
||||
"EnableHelpText": "Ativar criação do ficheiro de metadados para este tipo de metadados",
|
||||
"EnabledHelpText": "Ativar esta lista para uso no Radarr",
|
||||
"EnableCompletedDownloadHandlingHelpText": "Automaticamente importar transferências concluídas do cliente de transferências",
|
||||
"EnableColorImpairedModeHelpText": "Estilo alterado para permitir que utilizadores com daltonismo possam melhor distinguir informações de cores",
|
||||
"EnableCompletedDownloadHandlingHelpText": "Importar automaticamente as transferências concluídas do cliente de transferências",
|
||||
"EnableColorImpairedModeHelpText": "Estilo alterado para permitir que utilizadores com daltonismo possam distinguir melhor os códigos de cores",
|
||||
"EnableColorImpairedMode": "Ativar modo de daltonismo",
|
||||
"EnableAutomaticSearch": "Ativar pesquisa automática",
|
||||
"EnableAutomaticAdd": "Ativar adição automática",
|
||||
@@ -480,7 +480,7 @@
|
||||
"CreateEmptyMovieFoldersHelpText": "Criar pastas ausentes para filmes durante a análise do disco",
|
||||
"CreateEmptyMovieFolders": "Criar pastas vazias para filmes",
|
||||
"CopyUsingHardlinksHelpTextWarning": "Ocasionalmente, bloqueios de ficheiros podem impedir a renomeação de ficheiros que ainda estão sendo provisionados. Você pode temporariamente desativar o provisionamento e utilizar a função de renomeação do Radarr como uma solução alternativa.",
|
||||
"CopyUsingHardlinksHelpText": "Usar ligações fixas ao tentar copiar ficheiros de torrents que ainda estão em modo de provisionamento",
|
||||
"CopyUsingHardlinksHelpText": "Usar ligações fixas (Hardlinks) ao tentar copiar ficheiros de torrents que ainda estão em modo de semeio",
|
||||
"ConnectSettings": "Definições de ligação",
|
||||
"Conditions": "Condições",
|
||||
"ColonReplacementFormatHelpText": "Mude a forma como o Radarr lida com a substituição de dois-pontos",
|
||||
@@ -605,7 +605,7 @@
|
||||
"LoadingMovieExtraFilesFailed": "Falha no carregamento dos ficheiros adicionais do filme",
|
||||
"LoadingMovieCreditsFailed": "Falha no carregamento dos créditos do filme",
|
||||
"LinkHere": "aqui",
|
||||
"LastDuration": "Duração mais recente",
|
||||
"LastDuration": "Última Duração",
|
||||
"InteractiveSearch": "Pesquisa interativa",
|
||||
"IncludeRadarrRecommendations": "Incluir recomendações do Radarr",
|
||||
"ImportListSyncIntervalHelpText": "Frequência com que o Radarr sincroniza suas listas. Valor mínimo de 6 horas",
|
||||
@@ -619,7 +619,7 @@
|
||||
"GrabReleaseMessageText": "O Radarr não pode determinar a que filme pertence esta versão. O Radarr pode ser incapaz de importar automaticamente esta versão. Deseja capturar \"{0}\"?",
|
||||
"GoToInterp": "Ir para {0}",
|
||||
"ForMoreInformationOnTheIndividualImportListsClinkOnTheInfoButtons": "Para obter mais informações sobre cada lista de importação, clique nos botões de informação.",
|
||||
"ForMoreInformationOnTheIndividualDownloadClients": "Para obter mais informações sobre cada cliente de transferências, clique nos botões de informação.",
|
||||
"ForMoreInformationOnTheIndividualDownloadClients": "Para obter mais informações sobre cada cliente de transferências, clique nos botões de mais informação.",
|
||||
"FilterPlaceHolder": "Pesquisar filmes",
|
||||
"FailedLoadingSearchResults": "Erro ao carregar resultados da pesquisa. Tenta novamente.",
|
||||
"ExtraFileExtensionsHelpTexts2": "Exemplos: \".sub, .nfo\" ou \"sub,nfo\"",
|
||||
@@ -831,8 +831,8 @@
|
||||
"Tomorrow": "Amanhã",
|
||||
"Today": "Hoje",
|
||||
"ListTagsHelpText": "Os itens na lista de etiquetas serão adicionados com",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indexadores indisponíveis devido a erros por mais de 6 horas: {0}",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a erros por mais de 6 horas",
|
||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indexadores indisponíveis devido a erros à mais de 6 horas: {0}",
|
||||
"IndexerLongTermStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a erros á mais de 6 horas",
|
||||
"EditMovieFile": "Editar ficheiro do filme",
|
||||
"Yes": "Sim",
|
||||
"No": "Não",
|
||||
@@ -858,7 +858,7 @@
|
||||
"AddNotification": "Adicionar notificação",
|
||||
"AddQualityProfile": "Adicionar perfil de qualidade",
|
||||
"AddRootFolder": "Adicionar pasta raiz",
|
||||
"AddToDownloadQueue": "Adicionado à fila de transferências",
|
||||
"AddToDownloadQueue": "Adicionar à fila de transferências",
|
||||
"AfterManualRefresh": "Após a atualização manual",
|
||||
"AllFiles": "Todos os ficheiros",
|
||||
"Add": "Adicionar",
|
||||
@@ -1090,11 +1090,11 @@
|
||||
"Monox86SupportCheckMessage": "O sistema operativo atual é de 32 bits e não será suportado na próxima versão do Radarr.",
|
||||
"MonoBSDSupportCheckMessage": "O sistema operativo {0} não será compatível com a próxima versão do Radarr.",
|
||||
"Letterboxd": "Letterboxd",
|
||||
"ImportListMultipleMissingRoots": "Faltam várias pastas raiz para listas de importação: {0}",
|
||||
"ImportListMissingRoot": "Pasta raiz ausente para lista(s) de importação: {0}",
|
||||
"ImportListMultipleMissingRoots": "Faltam várias pastas de raiz para a(s) lista(s) de importação: {0}",
|
||||
"ImportListMissingRoot": "Pasta de raiz ausente para a(s) lista(s) de importação: {0}",
|
||||
"From": "De",
|
||||
"BypassDelayIfHighestQualityHelpText": "Ignorar o atraso quando a versão tiver a qualidade mais alta ativada no perfil de qualidade com o protocolo preferido",
|
||||
"BypassDelayIfHighestQuality": "Ignorar se for a qualidade mais alta",
|
||||
"BypassDelayIfHighestQuality": "Ignorar se a qualidade for mais alta",
|
||||
"NotificationTriggersHelpText": "Seleciona quais eventos acionam esta notificação",
|
||||
"UnableToAddRootFolder": "Não foi possível carregar as pastas raiz",
|
||||
"AreYouSureYouWantToRemoveTheSelectedItemsFromBlocklist": "Tem a certeza que quer remover os itens selecionados da lista de bloqueio?",
|
||||
@@ -1105,5 +1105,6 @@
|
||||
"UnableToLoadBlocklist": "Não foi possível carregar a lista de bloqueio",
|
||||
"Blocklisted": "Lista de bloqueio",
|
||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Tem a certeza que quer remover {0} item{1} da fila?",
|
||||
"BlocklistReleases": "Bloquear versão"
|
||||
"BlocklistReleases": "Bloquear versão",
|
||||
"IndexerTagHelpText": "Só use este indexador para filmes com pelo menos uma etiqueta correspondente. Deixe em branco para usar com todos os filmes."
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@
|
||||
"DeleteFilesLabel": "Excluir {0} arquivos do filme",
|
||||
"DeleteFilesHelpText": "Excluir os arquivos e a pasta do filme",
|
||||
"DeleteFile": "Excluir arquivo",
|
||||
"DeleteEmptyFoldersHelpText": "Excluir pastas de filmes vazias durante a verificação do disco e quando os arquivos de filmes forem excluídos",
|
||||
"DeleteEmptyFoldersHelpText": "Excluir pastas de filmes vazias durante a verificação do disco e quando os arquivos do filme forem excluídos",
|
||||
"DeleteEmptyFolders": "Excluir pastas vazias",
|
||||
"DeleteDownloadClientMessageText": "Tem certeza que deseja excluir o cliente de download \"{0}\"?",
|
||||
"DeleteDownloadClient": "Excluir cliente de download",
|
||||
@@ -326,14 +326,14 @@
|
||||
"ChmodFolder": "Pasta chmod",
|
||||
"CheckForFinishedDownloadsInterval": "Verifique o intervalo de downloads concluídos",
|
||||
"CheckDownloadClientForDetails": "verifique o cliente de download para saber mais",
|
||||
"ChangeHasNotBeenSavedYet": "A alteração ainda não foi salva",
|
||||
"ChangeFileDate": "Alterar data do arquivo",
|
||||
"ChangeHasNotBeenSavedYet": "Mudar o que não foi salvo ainda",
|
||||
"ChangeFileDate": "Mudar Data do Arquivo",
|
||||
"CertValidationNoLocal": "Desabilitado para endereços locais",
|
||||
"CertificationCountryHelpText": "Selecione o país para as certificações de filmes",
|
||||
"CertificationCountry": "País da certificação",
|
||||
"Certification": "Certificação",
|
||||
"CertificateValidationHelpText": "Alterar o quão estrita é a validação da certificação HTTPS",
|
||||
"CertificateValidation": "Validação de certificado",
|
||||
"CertificateValidationHelpText": "Mudar o quão estrito a validação da certificação HTTPS é",
|
||||
"CertificateValidation": "Validação do Certificado",
|
||||
"Cast": "Elenco",
|
||||
"CantFindMovie": "Por que não consigo encontrar meu filme?",
|
||||
"CancelProcessing": "Cancelar processamento",
|
||||
@@ -345,9 +345,9 @@
|
||||
"BuiltIn": "Embutido",
|
||||
"BranchUpdateMechanism": "Ramificação usada pelo mecanismo de atualização externo",
|
||||
"BranchUpdate": "Ramificação para atualização do Radarr",
|
||||
"Branch": "Ramificação",
|
||||
"BindAddressHelpText": "Endereço IP4 válido ou \"*\" para todas as interfaces",
|
||||
"BindAddress": "Endereço de vínculo",
|
||||
"Branch": "Ramo",
|
||||
"BindAddressHelpText": "Endereço IPv4 Válido ou '*' para todas as interfaces",
|
||||
"BindAddress": "Fixar Endereço",
|
||||
"BeforeUpdate": "Antes da atualização",
|
||||
"Backups": "Backups",
|
||||
"BackupRetentionHelpText": "Backups automáticos anteriores ao período de retenção serão limpos automaticamente",
|
||||
@@ -401,7 +401,7 @@
|
||||
"Mode": "Modo",
|
||||
"MissingNotMonitored": "Ausente (não monitorado)",
|
||||
"MissingMonitoredAndConsideredAvailable": "Ausente (monitorado)",
|
||||
"MissingFromDisk": "O Radarr não conseguiu encontrar o arquivo no disco, então ele foi removido",
|
||||
"MissingFromDisk": "Radarr não conseguiu encontrar o arquivo no disco, então o arquivo foi desvinculado do filme no banco de dados",
|
||||
"Missing": "Ausente",
|
||||
"MinutesSixty": "60 minutos: {0}",
|
||||
"MinutesNinety": "90 minutos: {0}",
|
||||
@@ -482,7 +482,7 @@
|
||||
"Agenda": "Programação",
|
||||
"Age": "Tempo de vida",
|
||||
"AfterManualRefresh": "Após atualização manual",
|
||||
"AddToDownloadQueue": "Adicionado à fila de download",
|
||||
"AddToDownloadQueue": "Adicionar à fila de download",
|
||||
"AddRootFolder": "Adicionar pasta raiz",
|
||||
"AddRestriction": "Adicionar restrição",
|
||||
"AddRemotePathMapping": "Adicionar mapeamento de caminho remoto",
|
||||
@@ -1101,7 +1101,7 @@
|
||||
"AreYouSureYouWantToRemoveTheSelectedItemsFromBlocklist": "Tem certeza de que deseja remover os itens selecionados da lista de bloqueio?",
|
||||
"Blocklist": "Lista de bloqueio",
|
||||
"BlocklistHelpText": "Impede que o Readarr obtenha este lançamento novamente de forma automática",
|
||||
"BlocklistRelease": "Adicionar versão à lista de bloqueio",
|
||||
"BlocklistRelease": "Lista de Bloqueio de Lançamentos",
|
||||
"RemoveFromBlocklist": "Remover da lista de bloqueio",
|
||||
"UnableToLoadBlocklist": "Não foi possível carregar a lista de bloqueio",
|
||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Você tem certeza de que deseja remover {0} item{1} da fila?",
|
||||
@@ -1111,5 +1111,8 @@
|
||||
"RemoveSelectedItem": "Remover Item Selecionado",
|
||||
"RemoveFailed": "Falha na remoção",
|
||||
"RemoveCompleted": "Remoção Completa",
|
||||
"RemoveDownloadsAlert": "As configurações de remoção foram movidas para as configurações do Cliente de Download na tabela acima."
|
||||
"RemoveDownloadsAlert": "As configurações de remoção foram movidas para as configurações do Cliente de Download na tabela acima.",
|
||||
"OnApplicationUpdate": "Na Atualização do Aplicativo",
|
||||
"OnApplicationUpdateHelpText": "Na Atualização do Aplicativo",
|
||||
"DiscordUrlInSlackNotification": "Você tem uma notificação do Discord configurado como uma notificação do Slack. Definir isso como uma notificação do Discord para melhor funcionalidade. Com efeito, notificações são: {0}"
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
"ImportHeader": "Импортировать фильмы, которые у вас уже есть",
|
||||
"ImportIncludeQuality": "Убедитесь, что имена файлов включают в себя их качество. Например {0}",
|
||||
"IncludeCustomFormatWhenRenaming": "Учесть пользовательский формат при переименовании",
|
||||
"ImportRootPath": "Указать Radarr папку со всеми фильмами, а не конкретным фильмом. Например {0}, а не {1}",
|
||||
"ImportRootPath": "Укажите Radarr на папку, содержащую все ваши фильмы, а не конкретный фильм. Например, {0}, а не {1}. Кроме того, каждый фильм должен находиться в своей отдельной папке в корневой папке/библиотеке.",
|
||||
"ImportTipsMessage": "Некоторые советы чтобы импорт прошел без проблем:",
|
||||
"InCinemas": "В кинотеатрах",
|
||||
"IncludeCustomFormatWhenRenamingHelpText": "Включить в {Custom Formats} формат переименования",
|
||||
@@ -231,18 +231,18 @@
|
||||
"ListTagsHelpText": "Тэги листа будут добавлены с",
|
||||
"LoadingMovieCreditsFailed": "Неудачная загрузка информации о фильме",
|
||||
"UnableToLoadCustomFormats": "Невозможно загрузить пользовательские форматы",
|
||||
"UpdateMechanismHelpText": "Используйте встроенную в Radarr функцию обновления или скрипт",
|
||||
"UpdateMechanismHelpText": "Используйте встроенное средство обновления Radarr или скрипт",
|
||||
"UpdateScriptPathHelpText": "Путь к пользовательскому скрипту, который обрабатывает остатки после процесса обновления",
|
||||
"CustomFormatJSON": "Настраиваемый формат JSON",
|
||||
"Add": "Добавить",
|
||||
"AddCustomFormat": "Добавить свой формат",
|
||||
"AddDelayProfile": "Добавить профиль задержки",
|
||||
"AddedToDownloadQueue": "Добавлено в очередь скачанных",
|
||||
"AddedToDownloadQueue": "Добавлено в очередь на скачивание",
|
||||
"AddNotification": "Добавить оповещение",
|
||||
"AddRootFolder": "Добавить корневой каталог",
|
||||
"AllFiles": "Все файлы",
|
||||
"AllMoviesInPathHaveBeenImported": "Все фильмы из {0} были внесены",
|
||||
"AddToDownloadQueue": "Добавлено в очередь скачивания",
|
||||
"AddToDownloadQueue": "Добавить в очередь загрузки",
|
||||
"AfterManualRefresh": "После обновления в ручную",
|
||||
"AllResultsHiddenFilter": "Все результаты скрыты фильтром",
|
||||
"AddDownloadClient": "Добавить программу для скачивания",
|
||||
@@ -251,7 +251,7 @@
|
||||
"ApplyTagsHelpTexts2": "Добавить: добавить ярлыки к существующему списку",
|
||||
"AptUpdater": "Используйте apt для установки обновления",
|
||||
"AuthForm": "Формы (Страница авторизации)",
|
||||
"AreYouSureYouWantToRemoveSelectedItemsFromQueue": "Вы уверены, что хотите удалить {1} номер{2} из очереди?",
|
||||
"AreYouSureYouWantToRemoveSelectedItemsFromQueue": "Вы уверены, что хотите удалить {0} элемент{1} из очереди?",
|
||||
"BeforeUpdate": "До обновления",
|
||||
"BuiltIn": "Встроено",
|
||||
"CalendarOptions": "Настройки календаря",
|
||||
@@ -335,7 +335,7 @@
|
||||
"Agenda": "План",
|
||||
"MinFormatScoreHelpText": "Минимальная оценка пользовательского формата для скачивания",
|
||||
"Cast": "Состав",
|
||||
"Certification": "Сертификация",
|
||||
"Certification": "Возрастной рейтинг",
|
||||
"Connect": "Подключить",
|
||||
"Connections": "Соединения",
|
||||
"CompletedDownloadHandling": "Обработка завершенных скачиваний",
|
||||
@@ -356,7 +356,7 @@
|
||||
"Automatic": "Автоматически",
|
||||
"AuthenticationMethodHelpText": "Необходим логин и пароль для доступа в Radarr",
|
||||
"ConnectionLostAutomaticMessage": "Radarr попытается соединиться автоматически или нажмите кнопку внизу.",
|
||||
"ConnectionLostMessage": "Radarr отключился от сервера и должен быть перезапущен.",
|
||||
"ConnectionLostMessage": "Radarr потерял связь с сервером и его необходимо перезагрузить, чтобы восстановить работоспособность.",
|
||||
"Crew": "Команда",
|
||||
"Health": "Здоровье",
|
||||
"EnableMediaInfoHelpText": "Извлекать из файлов видео разрешение, длительность и информацию о кодеках. Это может привести к высокой активности диска и сети во время сканирования.",
|
||||
@@ -421,10 +421,10 @@
|
||||
"RadarrSupportsCustomConditionsAgainstTheReleasePropertiesBelow": "Radarr поддерживает настраиваемые условия в соответствии со свойствами выпуска, указанными ниже.",
|
||||
"RadarrSupportsAnyRSSMovieListsAsWellAsTheOneStatedBelow": "Radarr поддерживает любые списки фильмов RSS, а также приведенный ниже.",
|
||||
"RadarrSupportsAnyIndexer": "Radarr поддерживает любой индексатор, использующий стандарт Newznab, а также другие индексаторы, перечисленные ниже.",
|
||||
"RadarrSupportsAnyDownloadClient": "Radarr поддерживает любой клиент загрузки использующий стандарт Newznab, а также другие клиенты загрузки перечисленные ниже.",
|
||||
"RadarrSupportsAnyDownloadClient": "Radarr поддерживает многие популярные торрент и usenet-клиенты для скачивания.",
|
||||
"RadarrCalendarFeed": "Лента календаря Radarr",
|
||||
"Radarr": "Radarr",
|
||||
"QuickImport": "Быстрый импорт",
|
||||
"QuickImport": "Автоматическое перемещение",
|
||||
"QueueIsEmpty": "Очередь пуста",
|
||||
"Queued": "В очереди",
|
||||
"Queue": "Очередь",
|
||||
@@ -438,7 +438,7 @@
|
||||
"QualityLimitsHelpText": "Пределы автоматически регулируются для времени воспроизведения фильма.",
|
||||
"QualityDefinitions": "Определения качества",
|
||||
"QualityCutoffHasNotBeenMet": "Порог качества не соблюден",
|
||||
"Quality": "Количество",
|
||||
"Quality": "Качество",
|
||||
"QualitiesHelpText": "Качества, расположенные выше в списке, более предпочтительны. Качества внутри одной группы равны. Требуются только отмеченные качества",
|
||||
"Qualities": "Качества",
|
||||
"PublishedDate": "Дата публикации",
|
||||
@@ -498,7 +498,7 @@
|
||||
"OrganizeModalDisabled": "Переименование отключено, переименовывать нечего",
|
||||
"OrganizeModalAllPathsRelative": "Все пути указаны относительно:",
|
||||
"OrganizeConfirm": "Вы действительно хотите организовать все файлы в {0} выбранных фильм(ах)?",
|
||||
"OrganizeAndRename": "Организовать & переименовать",
|
||||
"OrganizeAndRename": "Организовать и переименовать",
|
||||
"Organize": "Организовать",
|
||||
"Options": "Опции",
|
||||
"OpenThisModal": "Открыть это модальное окно",
|
||||
@@ -542,7 +542,7 @@
|
||||
"No": "Нет",
|
||||
"NextExecution": "Следующее выполнение",
|
||||
"New": "Новый",
|
||||
"NetCore": ".NET Core",
|
||||
"NetCore": ".NET",
|
||||
"Negated": "Отрицательный",
|
||||
"Negate": "Отрицать",
|
||||
"NamingSettings": "Настройки именования",
|
||||
@@ -597,7 +597,7 @@
|
||||
"MonoVersionCheckNotSupportedMessage": "Установленная Mono версия {0} больше не поддерживается. Обновите до версии {1}.",
|
||||
"MonoVersion": "Моно версия",
|
||||
"MonoTlsCheckMessage": "Radarr Mono 4.x tls включён, удалите настройку MONO_TLS_PROVIDER=legacy",
|
||||
"MonoNotNetCoreCheckMessage": "Пожалуйста обновите Radarr до .NET Core версии",
|
||||
"MonoNotNetCoreCheckMessage": "Пожалуйста обновите .NET Core версию Radarr, версии Mono не будут поддерживаться в следующем выпуске.",
|
||||
"MonitorMovie": "Отслеживать фильм",
|
||||
"MonitoredStatus": "Отслеживаемые/Статус",
|
||||
"MonitoredOnly": "Только отслеживаемые",
|
||||
@@ -608,7 +608,7 @@
|
||||
"Mode": "Режим",
|
||||
"MissingNotMonitored": "Не найден (не отслеживался)",
|
||||
"MissingMonitoredAndConsideredAvailable": "Не найден (отслеживался)",
|
||||
"MissingFromDisk": "Radarr не нашел файл на диске и он был удалён",
|
||||
"MissingFromDisk": "Radarr не смог найти файл на диске, поэтому файл был откреплён от фильма в базе данных",
|
||||
"Missing": "Не найдено",
|
||||
"MinutesSixty": "60 минут: {0}",
|
||||
"MinutesNinety": "90 минут: {0}",
|
||||
@@ -659,7 +659,7 @@
|
||||
"FreeSpace": "Свободное место",
|
||||
"ForMoreInformationOnTheIndividualIndexers": "Для дополнительной информации по индексаторам нажмите кнопку с информацией.",
|
||||
"ForMoreInformationOnTheIndividualImportListsClinkOnTheInfoButtons": "Для дополнительной информации по спискам импорта нажмите эту кнопку.",
|
||||
"ForMoreInformationOnTheIndividualDownloadClients": "Для дополнительной информации пл программам скачивания нажмите эту кнопку.",
|
||||
"ForMoreInformationOnTheIndividualDownloadClients": "Для получения дополнительной информации о каждом из клиентов загрузки нажмите на кнопки с дополнительной информацией.",
|
||||
"Formats": "Форматы",
|
||||
"Forecast": "Прогноз",
|
||||
"FollowPerson": "Отслеживать человека",
|
||||
@@ -678,7 +678,7 @@
|
||||
"FileNames": "Имена файлов",
|
||||
"Filename": "Имя файла",
|
||||
"FileManagement": "Управление файлами",
|
||||
"FileDateHelpText": "Заменить лату файла при импорте/сканировании",
|
||||
"FileDateHelpText": "Заменить дату файла при импорте/сканировании",
|
||||
"FeatureRequests": "Будущие запросы",
|
||||
"FailedToLoadQueue": "Не удалось загрузить очередность",
|
||||
"FailedToLoadMovieFromAPI": "Не удалось загрузить фильмы из API",
|
||||
@@ -875,7 +875,7 @@
|
||||
"SaveSettings": "Сохранить настройки",
|
||||
"SaveChanges": "Сохранить изменения",
|
||||
"Save": "Сохранить",
|
||||
"Runtime": "Время выполнения",
|
||||
"Runtime": "Продолжительность",
|
||||
"RSSSyncIntervalHelpTextWarning": "Это будет применяться ко всем индексаторам, пожалуйста, следуйте установленным ими правилам",
|
||||
"RSSSyncInterval": "Интервал синхронизации RSS",
|
||||
"RSSSync": "Синхронизация RSS",
|
||||
@@ -977,7 +977,7 @@
|
||||
"Socks5": "Socks5 (Поддержка TOR)",
|
||||
"Socks4": "Socks4",
|
||||
"Small": "Маленький",
|
||||
"SkipFreeSpaceCheckWhenImportingHelpText": "Используйте, когда Radarr не может обнаружить свободное место в корневой папке вашего фильма",
|
||||
"SkipFreeSpaceCheckWhenImportingHelpText": "Используется, когда Radarr не может найти свободное место в корневой папке фильма",
|
||||
"SkipFreeSpaceCheck": "Пропустить проверку свободного места",
|
||||
"SizeOnDisk": "Объём на диске",
|
||||
"Size": "Размер",
|
||||
@@ -1033,7 +1033,7 @@
|
||||
"SelectLanguges": "Выбрать языки",
|
||||
"SelectLanguage": "Выбрать язык",
|
||||
"SelectFolder": "Выбрать папку",
|
||||
"SelectDotDot": "'Выбрать...",
|
||||
"SelectDotDot": "Выбрать...",
|
||||
"SelectAll": "Выбрать все",
|
||||
"Seeders": "Сиды",
|
||||
"Security": "Безопасность",
|
||||
@@ -1094,5 +1094,22 @@
|
||||
"UnableToLoadBlocklist": "Не удалось загрузить черный список",
|
||||
"Blocklisted": "Черный список",
|
||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Вы действительно хотите удалить {0} из очереди?",
|
||||
"BlocklistReleases": "Релиз из черного списка"
|
||||
"BlocklistReleases": "Релиз из черного списка",
|
||||
"RemoveFailed": "Удаление не удалось",
|
||||
"RemoveSelectedItem": "Удалить выбранный элемент",
|
||||
"RemoveSelectedItems": "Удалить выбранные элементы",
|
||||
"BypassDelayIfHighestQuality": "Игнорировать при максимальном качестве",
|
||||
"BypassDelayIfHighestQualityHelpText": "Игнорирование задержки, когда выпуск имеет максимальное качество в выбранном профиле качества с предпочитаемым протоколом",
|
||||
"ImportListMissingRoot": "Отсутствует корневая папка для импортирования списка(ов): {0}",
|
||||
"ImportListMultipleMissingRoots": "Для импортируемых списков отсутствуют несколько корневых папок: {0}",
|
||||
"NotificationTriggersHelpText": "Выберите, какие события должны вызвать это уведомление",
|
||||
"RemotePathMappingCheckFileRemoved": "Файл {0} был удален в процессе обработки.",
|
||||
"RemotePathMappingCheckDockerFilesMissing": "Вы используете docker; клиент загрузки {0} сообщил о файлах в {1}, но этот каталог, похоже, не существует внутри контейнера. Проверьте соответствие путей к файлам и настройки тома контейнера.",
|
||||
"RemotePathMappingCheckDockerFolderMissing": "Вы используете docker; клиент загрузки {0} размещает загрузки в {1}, но этот каталог, похоже, не существует внутри контейнера. Проверьте соответствие путей к файлам и настройки тома контейнера.",
|
||||
"BlocklistHelpText": "Запрещает Radarr автоматически получать этот релиз повторно",
|
||||
"RemoveCompleted": "Удаление завершено",
|
||||
"RemoveDownloadsAlert": "Настройки удаления были перенесены в отдельные настройки клиента загрузки выше.",
|
||||
"TaskUserAgentTooltip": "User-Agent, представленный приложением, который вызывает API",
|
||||
"IndexerTagHelpText": "Используйте этот индексатор только для фильмов с хотя бы одним совпадающим тегом. Оставьте пустым, чтобы использовать для всех фильмов.",
|
||||
"DiscordUrlInSlackNotification": "У вас уведомление Discord настроено как уведомление Slack. Настройте это как уведомление Discord для лучшей функциональности. Затронуты следующие уведомления: {0}"
|
||||
}
|
||||
|
||||
@@ -110,5 +110,8 @@
|
||||
"DeleteDownloadClientMessageText": "Naozaj chcete zmazať značku formátu {0} ?",
|
||||
"DeleteIndexerMessageText": "Naozaj chcete zmazať značku formátu {0} ?",
|
||||
"DeleteListMessageText": "Naozaj chcete zmazať značku formátu {0} ?",
|
||||
"DeleteNotificationMessageText": "Naozaj chcete zmazať značku formátu {0} ?"
|
||||
"DeleteNotificationMessageText": "Naozaj chcete zmazať značku formátu {0} ?",
|
||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Naozaj chcete odobrať {0} položku {1} z fronty?",
|
||||
"ImportCustomFormat": "Pridať vlastný formát",
|
||||
"DeleteRestrictionHelpText": "Naozaj chcete zmazať tento profil oneskorenia?"
|
||||
}
|
||||
|
||||
1
src/NzbDrone.Core/Localization/Core/uk.json
Normal file
1
src/NzbDrone.Core/Localization/Core/uk.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user