mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-07 21:42:16 -07:00
New: Added option to filter Release Profile to a specific indexer
This commit is contained in:
parent
06c79b283c
commit
a035a78a2b
30 changed files with 309 additions and 66 deletions
|
@ -1,19 +1,8 @@
|
||||||
.enhancedSelect {
|
.enhancedSelect {
|
||||||
composes: input from '~Components/Form/Input.css';
|
composes: input from '~Components/Form/Input.css';
|
||||||
composes: link from '~Components/Link/Link.css';
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 6px 16px;
|
|
||||||
width: 100%;
|
|
||||||
height: 35px;
|
|
||||||
border: 1px solid $inputBorderColor;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: $white;
|
|
||||||
box-shadow: inset 0 1px 1px $inputBoxShadowColor;
|
|
||||||
color: $black;
|
|
||||||
cursor: default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hasError {
|
.hasError {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import CheckInput from './CheckInput';
|
||||||
import DeviceInputConnector from './DeviceInputConnector';
|
import DeviceInputConnector from './DeviceInputConnector';
|
||||||
import EnhancedSelectInput from './EnhancedSelectInput';
|
import EnhancedSelectInput from './EnhancedSelectInput';
|
||||||
import FormInputHelpText from './FormInputHelpText';
|
import FormInputHelpText from './FormInputHelpText';
|
||||||
|
import IndexerSelectInputConnector from './IndexerSelectInputConnector';
|
||||||
import KeyValueListInput from './KeyValueListInput';
|
import KeyValueListInput from './KeyValueListInput';
|
||||||
import MetadataProfileSelectInputConnector from './MetadataProfileSelectInputConnector';
|
import MetadataProfileSelectInputConnector from './MetadataProfileSelectInputConnector';
|
||||||
import MonitorAlbumsSelectInput from './MonitorAlbumsSelectInput';
|
import MonitorAlbumsSelectInput from './MonitorAlbumsSelectInput';
|
||||||
|
@ -69,6 +70,9 @@ function getComponent(type) {
|
||||||
case inputTypes.ALBUM_RELEASE_SELECT:
|
case inputTypes.ALBUM_RELEASE_SELECT:
|
||||||
return AlbumReleaseSelectInputConnector;
|
return AlbumReleaseSelectInputConnector;
|
||||||
|
|
||||||
|
case inputTypes.INDEXER_SELECT:
|
||||||
|
return IndexerSelectInputConnector;
|
||||||
|
|
||||||
case inputTypes.ROOT_FOLDER_SELECT:
|
case inputTypes.ROOT_FOLDER_SELECT:
|
||||||
return RootFolderSelectInputConnector;
|
return RootFolderSelectInputConnector;
|
||||||
|
|
||||||
|
|
102
frontend/src/Components/Form/IndexerSelectInputConnector.js
Normal file
102
frontend/src/Components/Form/IndexerSelectInputConnector.js
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
import _ from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { fetchIndexers } from 'Store/Actions/settingsActions';
|
||||||
|
import sortByName from 'Utilities/Array/sortByName';
|
||||||
|
import EnhancedSelectInput from './EnhancedSelectInput';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state) => state.settings.indexers,
|
||||||
|
(state, { includeAny }) => includeAny,
|
||||||
|
(indexers, includeAny) => {
|
||||||
|
const {
|
||||||
|
isFetching,
|
||||||
|
isPopulated,
|
||||||
|
error,
|
||||||
|
items
|
||||||
|
} = indexers;
|
||||||
|
|
||||||
|
const values = _.map(items.sort(sortByName), (indexer) => {
|
||||||
|
return {
|
||||||
|
key: indexer.id,
|
||||||
|
value: indexer.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (includeAny) {
|
||||||
|
values.unshift({
|
||||||
|
key: 0,
|
||||||
|
value: '(Any)'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isFetching,
|
||||||
|
isPopulated,
|
||||||
|
error,
|
||||||
|
values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
dispatchFetchIndexers: fetchIndexers
|
||||||
|
};
|
||||||
|
|
||||||
|
class IndexerSelectInputConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (!this.props.isPopulated) {
|
||||||
|
this.props.dispatchFetchIndexers();
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
values
|
||||||
|
} = this.props;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onChange = ({ name, value }) => {
|
||||||
|
this.props.onChange({ name, value: parseInt(value) });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<EnhancedSelectInput
|
||||||
|
{...this.props}
|
||||||
|
onChange={this.onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexerSelectInputConnector.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,
|
||||||
|
dispatchFetchIndexers: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
IndexerSelectInputConnector.defaultProps = {
|
||||||
|
includeAny: false
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, mapDispatchToProps)(IndexerSelectInputConnector);
|
|
@ -98,7 +98,9 @@ class KeyValueListInput extends Component {
|
||||||
className,
|
className,
|
||||||
value,
|
value,
|
||||||
keyPlaceholder,
|
keyPlaceholder,
|
||||||
valuePlaceholder
|
valuePlaceholder,
|
||||||
|
hasError,
|
||||||
|
hasWarning
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { isFocused } = this.state;
|
const { isFocused } = this.state;
|
||||||
|
@ -106,7 +108,9 @@ class KeyValueListInput extends Component {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(
|
<div className={classNames(
|
||||||
className,
|
className,
|
||||||
isFocused && styles.isFocused
|
isFocused && styles.isFocused,
|
||||||
|
hasError && styles.hasError,
|
||||||
|
hasWarning && styles.hasWarning
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,6 +12,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hasError {
|
||||||
|
composes: hasError from '~Components/Form/Input.css';
|
||||||
|
}
|
||||||
|
|
||||||
|
.hasWarning {
|
||||||
|
composes: hasWarning from '~Components/Form/Input.css';
|
||||||
|
}
|
||||||
|
|
||||||
.internalInput {
|
.internalInput {
|
||||||
flex: 1 1 0%;
|
flex: 1 1 0%;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
|
|
|
@ -210,6 +210,8 @@ class TagInput extends Component {
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
inputContainerClassName,
|
inputContainerClassName,
|
||||||
|
hasError,
|
||||||
|
hasWarning,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -226,7 +228,9 @@ class TagInput extends Component {
|
||||||
className={styles.internalInput}
|
className={styles.internalInput}
|
||||||
inputContainerClassName={classNames(
|
inputContainerClassName={classNames(
|
||||||
inputContainerClassName,
|
inputContainerClassName,
|
||||||
isFocused && styles.isFocused
|
isFocused && styles.isFocused,
|
||||||
|
hasError && styles.hasError,
|
||||||
|
hasWarning && styles.hasWarning
|
||||||
)}
|
)}
|
||||||
value={value}
|
value={value}
|
||||||
suggestions={suggestions}
|
suggestions={suggestions}
|
||||||
|
|
|
@ -12,6 +12,7 @@ export const PATH = 'path';
|
||||||
export const QUALITY_PROFILE_SELECT = 'qualityProfileSelect';
|
export const QUALITY_PROFILE_SELECT = 'qualityProfileSelect';
|
||||||
export const METADATA_PROFILE_SELECT = 'metadataProfileSelect';
|
export const METADATA_PROFILE_SELECT = 'metadataProfileSelect';
|
||||||
export const ALBUM_RELEASE_SELECT = 'albumReleaseSelect';
|
export const ALBUM_RELEASE_SELECT = 'albumReleaseSelect';
|
||||||
|
export const INDEXER_SELECT = 'indexerSelect';
|
||||||
export const ROOT_FOLDER_SELECT = 'rootFolderSelect';
|
export const ROOT_FOLDER_SELECT = 'rootFolderSelect';
|
||||||
export const SELECT = 'select';
|
export const SELECT = 'select';
|
||||||
export const SERIES_TYPE_SELECT = 'artistTypeSelect';
|
export const SERIES_TYPE_SELECT = 'artistTypeSelect';
|
||||||
|
@ -34,6 +35,7 @@ export const all = [
|
||||||
QUALITY_PROFILE_SELECT,
|
QUALITY_PROFILE_SELECT,
|
||||||
METADATA_PROFILE_SELECT,
|
METADATA_PROFILE_SELECT,
|
||||||
ALBUM_RELEASE_SELECT,
|
ALBUM_RELEASE_SELECT,
|
||||||
|
INDEXER_SELECT,
|
||||||
ROOT_FOLDER_SELECT,
|
ROOT_FOLDER_SELECT,
|
||||||
SELECT,
|
SELECT,
|
||||||
SERIES_TYPE_SELECT,
|
SERIES_TYPE_SELECT,
|
||||||
|
|
|
@ -30,11 +30,13 @@ function EditReleaseProfileModalContent(props) {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
|
enabled,
|
||||||
required,
|
required,
|
||||||
ignored,
|
ignored,
|
||||||
preferred,
|
preferred,
|
||||||
includePreferredWhenRenaming,
|
includePreferredWhenRenaming,
|
||||||
tags
|
tags,
|
||||||
|
indexerId
|
||||||
} = item;
|
} = item;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -45,6 +47,18 @@ function EditReleaseProfileModalContent(props) {
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<Form {...otherProps}>
|
<Form {...otherProps}>
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Enable Profile</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="enabled"
|
||||||
|
helpText="Check to enable release profile"
|
||||||
|
{...enabled}
|
||||||
|
onChange={onInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>Must Contain</FormLabel>
|
<FormLabel>Must Contain</FormLabel>
|
||||||
|
|
||||||
|
@ -99,9 +113,23 @@ function EditReleaseProfileModalContent(props) {
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="includePreferredWhenRenaming"
|
name="includePreferredWhenRenaming"
|
||||||
helpText="Include in {Preferred Words} renaming format"
|
helpText={indexerId.value === 0 ? 'Include in {Preferred Words} renaming format' : 'Only supported when Indexer is set to (All)'}
|
||||||
{...includePreferredWhenRenaming}
|
{...includePreferredWhenRenaming}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
|
isDisabled={indexerId.value !== 0}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Indexer</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.INDEXER_SELECT}
|
||||||
|
name="indexerId"
|
||||||
|
helpText="Specify what indexer the profile applies to"
|
||||||
|
{...indexerId}
|
||||||
|
includeAny={true}
|
||||||
|
onChange={onInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,13 @@ import selectSettings from 'Store/Selectors/selectSettings';
|
||||||
import EditReleaseProfileModalContent from './EditReleaseProfileModalContent';
|
import EditReleaseProfileModalContent from './EditReleaseProfileModalContent';
|
||||||
|
|
||||||
const newReleaseProfile = {
|
const newReleaseProfile = {
|
||||||
|
enabled: true,
|
||||||
required: '',
|
required: '',
|
||||||
ignored: '',
|
ignored: '',
|
||||||
preferred: [],
|
preferred: [],
|
||||||
includePreferredWhenRenaming: false,
|
includePreferredWhenRenaming: false,
|
||||||
tags: []
|
tags: [],
|
||||||
|
indexerId: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import _ from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Card from 'Components/Card';
|
import Card from 'Components/Card';
|
||||||
|
@ -55,11 +56,14 @@ class ReleaseProfile extends Component {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
|
enabled,
|
||||||
required,
|
required,
|
||||||
ignored,
|
ignored,
|
||||||
preferred,
|
preferred,
|
||||||
tags,
|
tags,
|
||||||
tagList
|
indexerId,
|
||||||
|
tagList,
|
||||||
|
indexerList
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -67,6 +71,8 @@ class ReleaseProfile extends Component {
|
||||||
isDeleteReleaseProfileModalOpen
|
isDeleteReleaseProfileModalOpen
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
|
const indexer = indexerId !== 0 && _.find(indexerList, { id: indexerId });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className={styles.releaseProfile}
|
className={styles.releaseProfile}
|
||||||
|
@ -92,6 +98,23 @@ class ReleaseProfile extends Component {
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
preferred.map((item) => {
|
||||||
|
const isPreferred = item.value >= 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label
|
||||||
|
key={item.key}
|
||||||
|
kind={isPreferred ? kinds.DEFAULT : kinds.WARNING}
|
||||||
|
>
|
||||||
|
{item.key} {isPreferred && '+'}{item.value}
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{
|
{
|
||||||
split(ignored).map((item) => {
|
split(ignored).map((item) => {
|
||||||
|
@ -111,28 +134,33 @@ class ReleaseProfile extends Component {
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
preferred.map((item) => {
|
|
||||||
const isPreferred = item.value >= 0;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
key={item.key}
|
|
||||||
kind={isPreferred ? kinds.DEFAULT : kinds.WARNING}
|
|
||||||
>
|
|
||||||
{item.key} {isPreferred && '+'}{item.value}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TagList
|
<TagList
|
||||||
tags={tags}
|
tags={tags}
|
||||||
tagList={tagList}
|
tagList={tagList}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
!enabled &&
|
||||||
|
<Label
|
||||||
|
kind={kinds.DISABLED}
|
||||||
|
outline={true}
|
||||||
|
>
|
||||||
|
Disabled
|
||||||
|
</Label>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
indexer &&
|
||||||
|
<Label
|
||||||
|
kind={kinds.INFO}
|
||||||
|
outline={true}
|
||||||
|
>
|
||||||
|
{indexer.name}
|
||||||
|
</Label>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<EditReleaseProfileModalConnector
|
<EditReleaseProfileModalConnector
|
||||||
id={id}
|
id={id}
|
||||||
isOpen={isEditReleaseProfileModalOpen}
|
isOpen={isEditReleaseProfileModalOpen}
|
||||||
|
@ -156,18 +184,23 @@ class ReleaseProfile extends Component {
|
||||||
|
|
||||||
ReleaseProfile.propTypes = {
|
ReleaseProfile.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
|
enabled: PropTypes.bool.isRequired,
|
||||||
required: PropTypes.string.isRequired,
|
required: PropTypes.string.isRequired,
|
||||||
ignored: PropTypes.string.isRequired,
|
ignored: PropTypes.string.isRequired,
|
||||||
preferred: PropTypes.arrayOf(PropTypes.object).isRequired,
|
preferred: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
|
indexerId: PropTypes.number.isRequired,
|
||||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
indexerList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
onConfirmDeleteReleaseProfile: PropTypes.func.isRequired
|
onConfirmDeleteReleaseProfile: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
ReleaseProfile.defaultProps = {
|
ReleaseProfile.defaultProps = {
|
||||||
|
enabled: true,
|
||||||
required: '',
|
required: '',
|
||||||
ignored: '',
|
ignored: '',
|
||||||
preferred: []
|
preferred: [],
|
||||||
|
indexerId: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ReleaseProfile;
|
export default ReleaseProfile;
|
||||||
|
|
|
@ -40,6 +40,7 @@ class ReleaseProfiles extends Component {
|
||||||
const {
|
const {
|
||||||
items,
|
items,
|
||||||
tagList,
|
tagList,
|
||||||
|
indexerList,
|
||||||
onConfirmDeleteReleaseProfile,
|
onConfirmDeleteReleaseProfile,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -69,6 +70,7 @@ class ReleaseProfiles extends Component {
|
||||||
<ReleaseProfile
|
<ReleaseProfile
|
||||||
key={item.id}
|
key={item.id}
|
||||||
tagList={tagList}
|
tagList={tagList}
|
||||||
|
indexerList={indexerList}
|
||||||
{...item}
|
{...item}
|
||||||
onConfirmDeleteReleaseProfile={onConfirmDeleteReleaseProfile}
|
onConfirmDeleteReleaseProfile={onConfirmDeleteReleaseProfile}
|
||||||
/>
|
/>
|
||||||
|
@ -92,6 +94,7 @@ ReleaseProfiles.propTypes = {
|
||||||
error: PropTypes.object,
|
error: PropTypes.object,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
indexerList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
onConfirmDeleteReleaseProfile: PropTypes.func.isRequired
|
onConfirmDeleteReleaseProfile: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,24 +2,28 @@ import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { deleteReleaseProfile, fetchReleaseProfiles } from 'Store/Actions/settingsActions';
|
import { deleteReleaseProfile, fetchIndexers, fetchReleaseProfiles } from 'Store/Actions/settingsActions';
|
||||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||||
import ReleaseProfiles from './ReleaseProfiles';
|
import ReleaseProfiles from './ReleaseProfiles';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.settings.releaseProfiles,
|
(state) => state.settings.releaseProfiles,
|
||||||
|
(state) => state.settings.indexers,
|
||||||
createTagsSelector(),
|
createTagsSelector(),
|
||||||
(releaseProfiles, tagList) => {
|
(releaseProfiles, indexers, tagList) => {
|
||||||
return {
|
return {
|
||||||
...releaseProfiles,
|
...releaseProfiles,
|
||||||
tagList
|
tagList,
|
||||||
|
isIndexersPopulated: indexers.isPopulated,
|
||||||
|
indexerList: indexers.items
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
|
fetchIndexers,
|
||||||
fetchReleaseProfiles,
|
fetchReleaseProfiles,
|
||||||
deleteReleaseProfile
|
deleteReleaseProfile
|
||||||
};
|
};
|
||||||
|
@ -31,6 +35,9 @@ class ReleaseProfilesConnector extends Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.fetchReleaseProfiles();
|
this.props.fetchReleaseProfiles();
|
||||||
|
if (!this.props.isIndexersPopulated) {
|
||||||
|
this.props.fetchIndexers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -54,8 +61,10 @@ class ReleaseProfilesConnector extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseProfilesConnector.propTypes = {
|
ReleaseProfilesConnector.propTypes = {
|
||||||
|
isIndexersPopulated: PropTypes.bool.isRequired,
|
||||||
fetchReleaseProfiles: PropTypes.func.isRequired,
|
fetchReleaseProfiles: PropTypes.func.isRequired,
|
||||||
deleteReleaseProfile: PropTypes.func.isRequired
|
deleteReleaseProfile: PropTypes.func.isRequired,
|
||||||
|
fetchIndexers: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(ReleaseProfilesConnector);
|
export default connect(createMapStateToProps, mapDispatchToProps)(ReleaseProfilesConnector);
|
||||||
|
|
|
@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Lidarr.Http;
|
using Lidarr.Http;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Indexers;
|
||||||
using NzbDrone.Core.Profiles.Releases;
|
using NzbDrone.Core.Profiles.Releases;
|
||||||
|
|
||||||
namespace Lidarr.Api.V1.Profiles.Release
|
namespace Lidarr.Api.V1.Profiles.Release
|
||||||
|
@ -9,10 +10,12 @@ namespace Lidarr.Api.V1.Profiles.Release
|
||||||
public class ReleaseProfileModule : LidarrRestModule<ReleaseProfileResource>
|
public class ReleaseProfileModule : LidarrRestModule<ReleaseProfileResource>
|
||||||
{
|
{
|
||||||
private readonly IReleaseProfileService _releaseProfileService;
|
private readonly IReleaseProfileService _releaseProfileService;
|
||||||
|
private readonly IIndexerFactory _indexerFactory;
|
||||||
|
|
||||||
public ReleaseProfileModule(IReleaseProfileService releaseProfileService)
|
public ReleaseProfileModule(IReleaseProfileService releaseProfileService, IIndexerFactory indexerFactory)
|
||||||
{
|
{
|
||||||
_releaseProfileService = releaseProfileService;
|
_releaseProfileService = releaseProfileService;
|
||||||
|
_indexerFactory = indexerFactory;
|
||||||
|
|
||||||
GetResourceById = GetById;
|
GetResourceById = GetById;
|
||||||
GetResourceAll = GetAll;
|
GetResourceAll = GetAll;
|
||||||
|
@ -26,6 +29,11 @@ namespace Lidarr.Api.V1.Profiles.Release
|
||||||
{
|
{
|
||||||
context.AddFailure("Either 'Must contain' or 'Must not contain' is required");
|
context.AddFailure("Either 'Must contain' or 'Must not contain' is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (restriction.Enabled && restriction.IndexerId != 0 && !_indexerFactory.Exists(restriction.IndexerId))
|
||||||
|
{
|
||||||
|
context.AddFailure(nameof(ReleaseProfile.IndexerId), "Indexer does not exist");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,12 @@ namespace Lidarr.Api.V1.Profiles.Release
|
||||||
{
|
{
|
||||||
public class ReleaseProfileResource : RestResource
|
public class ReleaseProfileResource : RestResource
|
||||||
{
|
{
|
||||||
|
public bool Enabled { get; set; }
|
||||||
public string Required { get; set; }
|
public string Required { get; set; }
|
||||||
public string Ignored { get; set; }
|
public string Ignored { get; set; }
|
||||||
public List<KeyValuePair<string, int>> Preferred { get; set; }
|
public List<KeyValuePair<string, int>> Preferred { get; set; }
|
||||||
public bool IncludePreferredWhenRenaming { get; set; }
|
public bool IncludePreferredWhenRenaming { get; set; }
|
||||||
|
public int IndexerId { get; set; }
|
||||||
public HashSet<int> Tags { get; set; }
|
public HashSet<int> Tags { get; set; }
|
||||||
|
|
||||||
public ReleaseProfileResource()
|
public ReleaseProfileResource()
|
||||||
|
@ -32,10 +34,12 @@ namespace Lidarr.Api.V1.Profiles.Release
|
||||||
{
|
{
|
||||||
Id = model.Id,
|
Id = model.Id,
|
||||||
|
|
||||||
|
Enabled = model.Enabled,
|
||||||
Required = model.Required,
|
Required = model.Required,
|
||||||
Ignored = model.Ignored,
|
Ignored = model.Ignored,
|
||||||
Preferred = model.Preferred,
|
Preferred = model.Preferred,
|
||||||
IncludePreferredWhenRenaming = model.IncludePreferredWhenRenaming,
|
IncludePreferredWhenRenaming = model.IncludePreferredWhenRenaming,
|
||||||
|
IndexerId = model.IndexerId,
|
||||||
Tags = new HashSet<int>(model.Tags)
|
Tags = new HashSet<int>(model.Tags)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -51,10 +55,12 @@ namespace Lidarr.Api.V1.Profiles.Release
|
||||||
{
|
{
|
||||||
Id = resource.Id,
|
Id = resource.Id,
|
||||||
|
|
||||||
|
Enabled = resource.Enabled,
|
||||||
Required = resource.Required,
|
Required = resource.Required,
|
||||||
Ignored = resource.Ignored,
|
Ignored = resource.Ignored,
|
||||||
Preferred = resource.Preferred,
|
Preferred = resource.Preferred,
|
||||||
IncludePreferredWhenRenaming = resource.IncludePreferredWhenRenaming,
|
IncludePreferredWhenRenaming = resource.IncludePreferredWhenRenaming,
|
||||||
|
IndexerId = resource.IndexerId,
|
||||||
Tags = new HashSet<int>(resource.Tags)
|
Tags = new HashSet<int>(resource.Tags)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
private void GivenRestictions(string required, string ignored)
|
private void GivenRestictions(string required, string ignored)
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IReleaseProfileService>()
|
Mocker.GetMock<IReleaseProfileService>()
|
||||||
.Setup(s => s.AllForTags(It.IsAny<HashSet<int>>()))
|
.Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>()))
|
||||||
.Returns(new List<ReleaseProfile>
|
.Returns(new List<ReleaseProfile>
|
||||||
{
|
{
|
||||||
new ReleaseProfile()
|
new ReleaseProfile()
|
||||||
|
@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
public void should_be_true_when_restrictions_are_empty()
|
public void should_be_true_when_restrictions_are_empty()
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IReleaseProfileService>()
|
Mocker.GetMock<IReleaseProfileService>()
|
||||||
.Setup(s => s.AllForTags(It.IsAny<HashSet<int>>()))
|
.Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>()))
|
||||||
.Returns(new List<ReleaseProfile>());
|
.Returns(new List<ReleaseProfile>());
|
||||||
|
|
||||||
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue();
|
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue();
|
||||||
|
@ -117,7 +117,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
_remoteAlbum.Release.Title = "[ www.Speed.cd ] - Katy Perry - Witness (2017) MP3 [320 kbps] ";
|
_remoteAlbum.Release.Title = "[ www.Speed.cd ] - Katy Perry - Witness (2017) MP3 [320 kbps] ";
|
||||||
|
|
||||||
Mocker.GetMock<IReleaseProfileService>()
|
Mocker.GetMock<IReleaseProfileService>()
|
||||||
.Setup(s => s.AllForTags(It.IsAny<HashSet<int>>()))
|
.Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>()))
|
||||||
.Returns(new List<ReleaseProfile>
|
.Returns(new List<ReleaseProfile>
|
||||||
{
|
{
|
||||||
new ReleaseProfile { Required = "320", Ignored = "www.Speed.cd" }
|
new ReleaseProfile { Required = "320", Ignored = "www.Speed.cd" }
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test.Profiles.Releases.PreferredWordService
|
||||||
});
|
});
|
||||||
|
|
||||||
Mocker.GetMock<IReleaseProfileService>()
|
Mocker.GetMock<IReleaseProfileService>()
|
||||||
.Setup(s => s.AllForTags(It.IsAny<HashSet<int>>()))
|
.Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>()))
|
||||||
.Returns(_releaseProfiles);
|
.Returns(_releaseProfiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,10 +51,10 @@ namespace NzbDrone.Core.Test.Profiles.Releases.PreferredWordService
|
||||||
public void should_return_0_when_there_are_no_release_profiles()
|
public void should_return_0_when_there_are_no_release_profiles()
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IReleaseProfileService>()
|
Mocker.GetMock<IReleaseProfileService>()
|
||||||
.Setup(s => s.AllForTags(It.IsAny<HashSet<int>>()))
|
.Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>()))
|
||||||
.Returns(new List<ReleaseProfile>());
|
.Returns(new List<ReleaseProfile>());
|
||||||
|
|
||||||
Subject.Calculate(_artist, _title).Should().Be(0);
|
Subject.Calculate(_artist, _title, 0).Should().Be(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -62,7 +62,7 @@ namespace NzbDrone.Core.Test.Profiles.Releases.PreferredWordService
|
||||||
{
|
{
|
||||||
GivenMatchingTerms();
|
GivenMatchingTerms();
|
||||||
|
|
||||||
Subject.Calculate(_artist, _title).Should().Be(0);
|
Subject.Calculate(_artist, _title, 0).Should().Be(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -70,7 +70,7 @@ namespace NzbDrone.Core.Test.Profiles.Releases.PreferredWordService
|
||||||
{
|
{
|
||||||
GivenMatchingTerms("24bit");
|
GivenMatchingTerms("24bit");
|
||||||
|
|
||||||
Subject.Calculate(_artist, _title).Should().Be(5);
|
Subject.Calculate(_artist, _title, 0).Should().Be(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -78,7 +78,7 @@ namespace NzbDrone.Core.Test.Profiles.Releases.PreferredWordService
|
||||||
{
|
{
|
||||||
GivenMatchingTerms("16bit");
|
GivenMatchingTerms("16bit");
|
||||||
|
|
||||||
Subject.Calculate(_artist, _title).Should().Be(-10);
|
Subject.Calculate(_artist, _title, 0).Should().Be(-10);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -88,7 +88,7 @@ namespace NzbDrone.Core.Test.Profiles.Releases.PreferredWordService
|
||||||
|
|
||||||
GivenMatchingTerms("24bit");
|
GivenMatchingTerms("24bit");
|
||||||
|
|
||||||
Subject.Calculate(_artist, _title).Should().Be(10);
|
Subject.Calculate(_artist, _title, 0).Should().Be(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace NzbDrone.Core.Test.Profiles.Releases.PreferredWordService
|
||||||
private void GivenReleaseProfile()
|
private void GivenReleaseProfile()
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IReleaseProfileService>()
|
Mocker.GetMock<IReleaseProfileService>()
|
||||||
.Setup(s => s.AllForTags(It.IsAny<HashSet<int>>()))
|
.Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>()))
|
||||||
.Returns(_releaseProfiles);
|
.Returns(_releaseProfiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.Profiles.Releases.PreferredWordService
|
||||||
public void should_return_empty_list_when_there_are_no_release_profiles()
|
public void should_return_empty_list_when_there_are_no_release_profiles()
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IReleaseProfileService>()
|
Mocker.GetMock<IReleaseProfileService>()
|
||||||
.Setup(s => s.AllForTags(It.IsAny<HashSet<int>>()))
|
.Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>()))
|
||||||
.Returns(new List<ReleaseProfile>());
|
.Returns(new List<ReleaseProfile>());
|
||||||
|
|
||||||
Subject.GetMatchingPreferredWords(_artist, _title).Should().BeEmpty();
|
Subject.GetMatchingPreferredWords(_artist, _title).Should().BeEmpty();
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
IEnumerable<TModel> All();
|
IEnumerable<TModel> All();
|
||||||
int Count();
|
int Count();
|
||||||
|
TModel Find(int id);
|
||||||
TModel Get(int id);
|
TModel Get(int id);
|
||||||
TModel Insert(TModel model);
|
TModel Insert(TModel model);
|
||||||
TModel Update(TModel model);
|
TModel Update(TModel model);
|
||||||
|
@ -87,10 +88,17 @@ namespace NzbDrone.Core.Datastore
|
||||||
return Query(Builder());
|
return Query(Builder());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TModel Get(int id)
|
public TModel Find(int id)
|
||||||
{
|
{
|
||||||
var model = Query(x => x.Id == id).FirstOrDefault();
|
var model = Query(x => x.Id == id).FirstOrDefault();
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TModel Get(int id)
|
||||||
|
{
|
||||||
|
var model = Find(id);
|
||||||
|
|
||||||
if (model == null)
|
if (model == null)
|
||||||
{
|
{
|
||||||
throw new ModelNotFoundException(typeof(TModel), id);
|
throw new ModelNotFoundException(typeof(TModel), id);
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
using FluentMigrator;
|
||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
{
|
||||||
|
[Migration(041)]
|
||||||
|
public class add_indexer_and_enabled_to_release_profiles : NzbDroneMigrationBase
|
||||||
|
{
|
||||||
|
protected override void MainDbUpgrade()
|
||||||
|
{
|
||||||
|
Alter.Table("ReleaseProfiles").AddColumn("Enabled").AsBoolean().WithDefaultValue(true);
|
||||||
|
Alter.Table("ReleaseProfiles").AddColumn("IndexerId").AsInt32().WithDefaultValue(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,7 +58,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
if (!_upgradableSpecification.CutoffNotMet(qualityProfile,
|
if (!_upgradableSpecification.CutoffNotMet(qualityProfile,
|
||||||
currentQualities,
|
currentQualities,
|
||||||
_preferredWordServiceCalculator.Calculate(subject.Artist, trackFiles[0].GetSceneOrFileName()),
|
_preferredWordServiceCalculator.Calculate(subject.Artist, trackFiles[0].GetSceneOrFileName(), subject.Release.IndexerId),
|
||||||
subject.ParsedAlbumInfo.Quality,
|
subject.ParsedAlbumInfo.Quality,
|
||||||
subject.PreferredWordScore))
|
subject.PreferredWordScore))
|
||||||
{
|
{
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
_logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteAlbum.ParsedAlbumInfo.Quality);
|
_logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteAlbum.ParsedAlbumInfo.Quality);
|
||||||
|
|
||||||
var queuedItemPreferredWordScore = _preferredWordServiceCalculator.Calculate(subject.Artist, queueItem.Title);
|
var queuedItemPreferredWordScore = _preferredWordServiceCalculator.Calculate(subject.Artist, queueItem.Title, subject.Release?.IndexerId ?? 0);
|
||||||
|
|
||||||
if (!_upgradableSpecification.CutoffNotMet(qualityProfile,
|
if (!_upgradableSpecification.CutoffNotMet(qualityProfile,
|
||||||
new List<QualityModel> { remoteAlbum.ParsedAlbumInfo.Quality },
|
new List<QualityModel> { remoteAlbum.ParsedAlbumInfo.Quality },
|
||||||
|
|
|
@ -30,10 +30,10 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
_logger.Debug("Checking if release meets restrictions: {0}", subject);
|
_logger.Debug("Checking if release meets restrictions: {0}", subject);
|
||||||
|
|
||||||
var title = subject.Release.Title;
|
var title = subject.Release.Title;
|
||||||
var restrictions = _releaseProfileService.AllForTags(subject.Artist.Tags);
|
var releaseProfiles = _releaseProfileService.EnabledForTags(subject.Artist.Tags, subject.Release.IndexerId);
|
||||||
|
|
||||||
var required = restrictions.Where(r => r.Required.IsNotNullOrWhiteSpace());
|
var required = releaseProfiles.Where(r => r.Required.IsNotNullOrWhiteSpace());
|
||||||
var ignored = restrictions.Where(r => r.Ignored.IsNotNullOrWhiteSpace());
|
var ignored = releaseProfiles.Where(r => r.Ignored.IsNotNullOrWhiteSpace());
|
||||||
|
|
||||||
foreach (var r in required)
|
foreach (var r in required)
|
||||||
{
|
{
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||||
|
|
||||||
// The artist will be the same as the one in history since it's the same album.
|
// The artist will be the same as the one in history since it's the same album.
|
||||||
// Instead of fetching the artist from the DB reuse the known artist.
|
// Instead of fetching the artist from the DB reuse the known artist.
|
||||||
var preferredWordScore = _preferredWordServiceCalculator.Calculate(subject.Artist, mostRecent.SourceTitle);
|
var preferredWordScore = _preferredWordServiceCalculator.Calculate(subject.Artist, mostRecent.SourceTitle, subject.Release?.IndexerId ?? 0);
|
||||||
|
|
||||||
var cutoffUnmet = _upgradableSpecification.CutoffNotMet(
|
var cutoffUnmet = _upgradableSpecification.CutoffNotMet(
|
||||||
subject.Artist.QualityProfile,
|
subject.Artist.QualityProfile,
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
|
||||||
if (!_upgradableSpecification.IsUpgradable(subject.Artist.QualityProfile,
|
if (!_upgradableSpecification.IsUpgradable(subject.Artist.QualityProfile,
|
||||||
currentQualities,
|
currentQualities,
|
||||||
_preferredWordServiceCalculator.Calculate(subject.Artist, trackFiles[0].GetSceneOrFileName()),
|
_preferredWordServiceCalculator.Calculate(subject.Artist, trackFiles[0].GetSceneOrFileName(), subject.Release?.IndexerId ?? 0),
|
||||||
subject.ParsedAlbumInfo.Quality,
|
subject.ParsedAlbumInfo.Quality,
|
||||||
subject.PreferredWordScore))
|
subject.PreferredWordScore))
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace NzbDrone.Core.Download.Aggregation.Aggregators
|
||||||
|
|
||||||
public RemoteAlbum Aggregate(RemoteAlbum remoteAlbum)
|
public RemoteAlbum Aggregate(RemoteAlbum remoteAlbum)
|
||||||
{
|
{
|
||||||
remoteAlbum.PreferredWordScore = _preferredWordServiceCalculator.Calculate(remoteAlbum.Artist, remoteAlbum.Release.Title);
|
remoteAlbum.PreferredWordScore = _preferredWordServiceCalculator.Calculate(remoteAlbum.Artist, remoteAlbum.Release.Title, remoteAlbum.Release.IndexerId);
|
||||||
|
|
||||||
return remoteAlbum;
|
return remoteAlbum;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||||
{
|
{
|
||||||
public interface IPreferredWordService
|
public interface IPreferredWordService
|
||||||
{
|
{
|
||||||
int Calculate(Artist artist, string title);
|
int Calculate(Artist artist, string title, int indexerId);
|
||||||
List<string> GetMatchingPreferredWords(Artist artist, string title);
|
List<string> GetMatchingPreferredWords(Artist artist, string title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,11 +25,11 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Calculate(Artist series, string title)
|
public int Calculate(Artist artist, string title, int indexerId)
|
||||||
{
|
{
|
||||||
_logger.Trace("Calculating preferred word score for '{0}'", title);
|
_logger.Trace("Calculating preferred word score for '{0}'", title);
|
||||||
|
|
||||||
var releaseProfiles = _releaseProfileService.AllForTags(series.Tags);
|
var releaseProfiles = _releaseProfileService.EnabledForTags(artist.Tags, indexerId);
|
||||||
var matchingPairs = new List<KeyValuePair<string, int>>();
|
var matchingPairs = new List<KeyValuePair<string, int>>();
|
||||||
|
|
||||||
foreach (var releaseProfile in releaseProfiles)
|
foreach (var releaseProfile in releaseProfiles)
|
||||||
|
@ -54,7 +54,7 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||||
|
|
||||||
public List<string> GetMatchingPreferredWords(Artist artist, string title)
|
public List<string> GetMatchingPreferredWords(Artist artist, string title)
|
||||||
{
|
{
|
||||||
var releaseProfiles = _releaseProfileService.AllForTags(artist.Tags);
|
var releaseProfiles = _releaseProfileService.EnabledForTags(artist.Tags, 0);
|
||||||
var matchingPairs = new List<KeyValuePair<string, int>>();
|
var matchingPairs = new List<KeyValuePair<string, int>>();
|
||||||
|
|
||||||
_logger.Trace("Calculating preferred word score for '{0}'", title);
|
_logger.Trace("Calculating preferred word score for '{0}'", title);
|
||||||
|
|
|
@ -5,17 +5,21 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||||
{
|
{
|
||||||
public class ReleaseProfile : ModelBase
|
public class ReleaseProfile : ModelBase
|
||||||
{
|
{
|
||||||
|
public bool Enabled { get; set; }
|
||||||
public string Required { get; set; }
|
public string Required { get; set; }
|
||||||
public string Ignored { get; set; }
|
public string Ignored { get; set; }
|
||||||
public List<KeyValuePair<string, int>> Preferred { get; set; }
|
public List<KeyValuePair<string, int>> Preferred { get; set; }
|
||||||
public bool IncludePreferredWhenRenaming { get; set; }
|
public bool IncludePreferredWhenRenaming { get; set; }
|
||||||
|
public int IndexerId { get; set; }
|
||||||
public HashSet<int> Tags { get; set; }
|
public HashSet<int> Tags { get; set; }
|
||||||
|
|
||||||
public ReleaseProfile()
|
public ReleaseProfile()
|
||||||
{
|
{
|
||||||
|
Enabled = true;
|
||||||
Preferred = new List<KeyValuePair<string, int>>();
|
Preferred = new List<KeyValuePair<string, int>>();
|
||||||
IncludePreferredWhenRenaming = true;
|
IncludePreferredWhenRenaming = true;
|
||||||
Tags = new HashSet<int>();
|
Tags = new HashSet<int>();
|
||||||
|
IndexerId = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||||
List<ReleaseProfile> All();
|
List<ReleaseProfile> All();
|
||||||
List<ReleaseProfile> AllForTag(int tagId);
|
List<ReleaseProfile> AllForTag(int tagId);
|
||||||
List<ReleaseProfile> AllForTags(HashSet<int> tagIds);
|
List<ReleaseProfile> AllForTags(HashSet<int> tagIds);
|
||||||
|
List<ReleaseProfile> EnabledForTags(HashSet<int> tagIds, int indexerId);
|
||||||
ReleaseProfile Get(int id);
|
ReleaseProfile Get(int id);
|
||||||
void Delete(int id);
|
void Delete(int id);
|
||||||
ReleaseProfile Add(ReleaseProfile restriction);
|
ReleaseProfile Add(ReleaseProfile restriction);
|
||||||
|
@ -48,6 +49,13 @@ namespace NzbDrone.Core.Profiles.Releases
|
||||||
return _repo.All().Where(r => r.Tags.Intersect(tagIds).Any() || r.Tags.Empty()).ToList();
|
return _repo.All().Where(r => r.Tags.Intersect(tagIds).Any() || r.Tags.Empty()).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ReleaseProfile> EnabledForTags(HashSet<int> tagIds, int indexerId)
|
||||||
|
{
|
||||||
|
return AllForTags(tagIds)
|
||||||
|
.Where(r => r.Enabled)
|
||||||
|
.Where(r => r.IndexerId == indexerId || r.IndexerId == 0).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public ReleaseProfile Get(int id)
|
public ReleaseProfile Get(int id)
|
||||||
{
|
{
|
||||||
return _repo.Get(id);
|
return _repo.Get(id);
|
||||||
|
|
|
@ -9,6 +9,7 @@ namespace NzbDrone.Core.ThingiProvider
|
||||||
{
|
{
|
||||||
List<TProviderDefinition> All();
|
List<TProviderDefinition> All();
|
||||||
List<TProvider> GetAvailableProviders();
|
List<TProvider> GetAvailableProviders();
|
||||||
|
bool Exists(int id);
|
||||||
TProviderDefinition Get(int id);
|
TProviderDefinition Get(int id);
|
||||||
TProviderDefinition Create(TProviderDefinition definition);
|
TProviderDefinition Create(TProviderDefinition definition);
|
||||||
void Update(TProviderDefinition definition);
|
void Update(TProviderDefinition definition);
|
||||||
|
|
|
@ -91,6 +91,11 @@ namespace NzbDrone.Core.ThingiProvider
|
||||||
return Active().Select(GetInstance).ToList();
|
return Active().Select(GetInstance).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Exists(int id)
|
||||||
|
{
|
||||||
|
return _providerRepository.Find(id) != null;
|
||||||
|
}
|
||||||
|
|
||||||
public TProviderDefinition Get(int id)
|
public TProviderDefinition Get(int id)
|
||||||
{
|
{
|
||||||
return _providerRepository.Get(id);
|
return _providerRepository.Get(id);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue