1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-18 21:35:51 -04:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Qstick dec77f63e7 Speed up collections load by 50% 2022-11-23 21:03:52 -06:00
357 changed files with 4076 additions and 6710 deletions
+11 -15
View File
@@ -19,10 +19,10 @@ indent_size = 4
dotnet_sort_system_directives_first = true dotnet_sort_system_directives_first = true
# Avoid "this." and "Me." if not necessary # Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:refactoring dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:refactoring dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:refactoring dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:refactoring dotnet_style_qualification_for_event = false:warning
# Indentation preferences # Indentation preferences
csharp_indent_block_contents = true csharp_indent_block_contents = true
@@ -32,10 +32,6 @@ csharp_indent_case_contents_when_block = true
csharp_indent_switch_labels = true csharp_indent_switch_labels = true
csharp_indent_labels = flush_left csharp_indent_labels = flush_left
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_naming_style.instance_field_style.capitalization = camel_case dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _ dotnet_naming_style.instance_field_style.required_prefix = _
@@ -68,7 +64,6 @@ dotnet_diagnostic.SA1406.severity = suggestion
dotnet_diagnostic.SA1410.severity = suggestion dotnet_diagnostic.SA1410.severity = suggestion
dotnet_diagnostic.SA1411.severity = suggestion dotnet_diagnostic.SA1411.severity = suggestion
dotnet_diagnostic.SA1413.severity = none dotnet_diagnostic.SA1413.severity = none
dotnet_diagnostic.SA1512.severity = none
dotnet_diagnostic.SA1516.severity = none dotnet_diagnostic.SA1516.severity = none
dotnet_diagnostic.SA1600.severity = none dotnet_diagnostic.SA1600.severity = none
dotnet_diagnostic.SA1601.severity = none dotnet_diagnostic.SA1601.severity = none
@@ -167,7 +162,6 @@ dotnet_diagnostic.CA1309.severity = suggestion
dotnet_diagnostic.CA1310.severity = suggestion dotnet_diagnostic.CA1310.severity = suggestion
dotnet_diagnostic.CA1401.severity = suggestion dotnet_diagnostic.CA1401.severity = suggestion
dotnet_diagnostic.CA1416.severity = suggestion dotnet_diagnostic.CA1416.severity = suggestion
dotnet_diagnostic.CA1419.severity = suggestion
dotnet_diagnostic.CA1507.severity = suggestion dotnet_diagnostic.CA1507.severity = suggestion
dotnet_diagnostic.CA1508.severity = suggestion dotnet_diagnostic.CA1508.severity = suggestion
dotnet_diagnostic.CA1707.severity = suggestion dotnet_diagnostic.CA1707.severity = suggestion
@@ -183,6 +177,9 @@ dotnet_diagnostic.CA1720.severity = suggestion
dotnet_diagnostic.CA1721.severity = suggestion dotnet_diagnostic.CA1721.severity = suggestion
dotnet_diagnostic.CA1724.severity = suggestion dotnet_diagnostic.CA1724.severity = suggestion
dotnet_diagnostic.CA1725.severity = suggestion dotnet_diagnostic.CA1725.severity = suggestion
dotnet_diagnostic.CA1801.severity = suggestion
dotnet_diagnostic.CA1802.severity = suggestion
dotnet_diagnostic.CA1805.severity = suggestion
dotnet_diagnostic.CA1806.severity = suggestion dotnet_diagnostic.CA1806.severity = suggestion
dotnet_diagnostic.CA1810.severity = suggestion dotnet_diagnostic.CA1810.severity = suggestion
dotnet_diagnostic.CA1812.severity = suggestion dotnet_diagnostic.CA1812.severity = suggestion
@@ -194,11 +191,13 @@ dotnet_diagnostic.CA1819.severity = suggestion
dotnet_diagnostic.CA1822.severity = suggestion dotnet_diagnostic.CA1822.severity = suggestion
dotnet_diagnostic.CA1823.severity = suggestion dotnet_diagnostic.CA1823.severity = suggestion
dotnet_diagnostic.CA1824.severity = suggestion dotnet_diagnostic.CA1824.severity = suggestion
dotnet_diagnostic.CA1848.severity = suggestion
dotnet_diagnostic.CA2000.severity = suggestion dotnet_diagnostic.CA2000.severity = suggestion
dotnet_diagnostic.CA2002.severity = suggestion dotnet_diagnostic.CA2002.severity = suggestion
dotnet_diagnostic.CA2007.severity = suggestion dotnet_diagnostic.CA2007.severity = suggestion
dotnet_diagnostic.CA2008.severity = suggestion dotnet_diagnostic.CA2008.severity = suggestion
dotnet_diagnostic.CA2009.severity = suggestion
dotnet_diagnostic.CA2010.severity = suggestion
dotnet_diagnostic.CA2011.severity = suggestion
dotnet_diagnostic.CA2012.severity = suggestion dotnet_diagnostic.CA2012.severity = suggestion
dotnet_diagnostic.CA2013.severity = suggestion dotnet_diagnostic.CA2013.severity = suggestion
dotnet_diagnostic.CA2100.severity = suggestion dotnet_diagnostic.CA2100.severity = suggestion
@@ -229,9 +228,6 @@ dotnet_diagnostic.CA2243.severity = suggestion
dotnet_diagnostic.CA2244.severity = suggestion dotnet_diagnostic.CA2244.severity = suggestion
dotnet_diagnostic.CA2245.severity = suggestion dotnet_diagnostic.CA2245.severity = suggestion
dotnet_diagnostic.CA2246.severity = suggestion dotnet_diagnostic.CA2246.severity = suggestion
dotnet_diagnostic.CA2249.severity = suggestion
dotnet_diagnostic.CA2251.severity = suggestion
dotnet_diagnostic.CA2254.severity = suggestion
dotnet_diagnostic.CA3061.severity = suggestion dotnet_diagnostic.CA3061.severity = suggestion
dotnet_diagnostic.CA3075.severity = suggestion dotnet_diagnostic.CA3075.severity = suggestion
dotnet_diagnostic.CA3076.severity = suggestion dotnet_diagnostic.CA3076.severity = suggestion
@@ -259,7 +255,7 @@ dotnet_diagnostic.CA5392.severity = suggestion
dotnet_diagnostic.CA5394.severity = suggestion dotnet_diagnostic.CA5394.severity = suggestion
dotnet_diagnostic.CA5397.severity = suggestion dotnet_diagnostic.CA5397.severity = suggestion
dotnet_diagnostic.SYSLIB0006.severity = none
[*.{js,html,js,hbs,less,css}] [*.{js,html,js,hbs,less,css}]
charset = utf-8 charset = utf-8
+9
View File
@@ -0,0 +1,9 @@
{
"paths": [
"frontend/src/**/*.js"
],
"ignored": [
"**/node_modules/**/*"
],
"port": 5004
}
+45
View File
@@ -0,0 +1,45 @@
name: Sync issue to Azure DevOps work item
on:
issues:
types:
[opened, edited, deleted, closed, reopened, labeled, unlabeled, assigned]
concurrency: azuresync-${{ github.event.issue.number }}
permissions: {}
jobs:
alert:
permissions:
issues: write # to update issue body
runs-on: ubuntu-latest
steps:
- uses: danhellem/github-actions-issue-to-work-item@master
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == true }}"
env:
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
github_token: "${{ github.token }}"
ado_organization: "Servarr"
ado_project: "Servarr"
ado_area_path: "Servarr\\Radarr"
ado_wit: "Bug"
ado_new_state: "New"
ado_active_state: "Active"
ado_close_state: "Closed"
ado_bypassrules: true
log_level: 100
- uses: danhellem/github-actions-issue-to-work-item@master
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == false }}"
env:
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
github_token: "${{ github.token }}"
ado_organization: "Servarr"
ado_project: "Servarr"
ado_area_path: "Servarr\\Radarr"
ado_wit: "User Story"
ado_new_state: "New"
ado_active_state: "Active"
ado_close_state: "Closed"
ado_bypassrules: true
log_level: 100
+21 -2
View File
@@ -166,8 +166,27 @@ packages.config.md5sum
# Common IntelliJ Platform excludes # Common IntelliJ Platform excludes
# Ignore Rider projects completely for now # User specific
.idea/ **/.idea/**/workspace.xml
**/.idea/**/tasks.xml
**/.idea/shelf/*
**/.idea/dictionaries
**/.idea/.idea.Radarr.Posix
**/.idea/.idea.Radarr.Windows
# Sensitive or high-churn files
**/.idea/**/dataSources/
**/.idea/**/dataSources.ids
**/.idea/**/dataSources.xml
**/.idea/**/dataSources.local.xml
**/.idea/**/sqlDataSources.xml
**/.idea/**/dynamic.xml
# Rider
# Rider auto-generates .iml files, and contentModel.xml
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
# ignore node_modules symlink # ignore node_modules symlink
node_modules node_modules
-9
View File
@@ -76,15 +76,6 @@ Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrai
* [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/) * [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
* [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/) * [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
## DigitalOcean
This project is also supported by DigitalOcean
<p>
<a href="https://www.digitalocean.com/">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
### License ### License
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html) * [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
+2 -2
View File
@@ -9,13 +9,13 @@ variables:
testsFolder: './_tests' testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '4.4.4' majorVersion: '4.3.1'
minorVersion: $[counter('minorVersion', 2000)] minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)' radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)' buildName: '$(Build.SourceBranchName).$(radarrVersion)'
sentryOrg: 'servarr' sentryOrg: 'servarr'
sentryUrl: 'https://sentry.servarr.com' sentryUrl: 'https://sentry.servarr.com'
dotnetVersion: '6.0.408' dotnetVersion: '6.0.400'
nodeVersion: '16.X' nodeVersion: '16.X'
innoVersion: '6.2.0' innoVersion: '6.2.0'
windowsImage: 'windows-2022' windowsImage: 'windows-2022'
@@ -1,4 +1,3 @@
// eslint-disable-next-line filenames/match-exported
const loaderUtils = require('loader-utils'); const loaderUtils = require('loader-utils');
module.exports = function cssVariablesLoader(source) { module.exports = function cssVariablesLoader(source) {
+2 -4
View File
@@ -56,7 +56,6 @@ class HistoryRow extends Component {
movie, movie,
quality, quality,
customFormats, customFormats,
customFormatScore,
languages, languages,
qualityCutoffNotMet, qualityCutoffNotMet,
eventType, eventType,
@@ -176,7 +175,7 @@ class HistoryRow extends Component {
key={name} key={name}
className={styles.customFormatScore} className={styles.customFormatScore}
> >
{formatCustomFormatScore(customFormatScore)} {formatCustomFormatScore(data.customFormatScore)}
</TableRowCell> </TableRowCell>
); );
} }
@@ -242,9 +241,8 @@ HistoryRow.propTypes = {
movie: PropTypes.object.isRequired, movie: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired, languages: PropTypes.arrayOf(PropTypes.object).isRequired,
quality: PropTypes.object.isRequired, quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),
customFormatScore: PropTypes.number.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired, qualityCutoffNotMet: PropTypes.bool.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
eventType: PropTypes.string.isRequired, eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired, sourceTitle: PropTypes.string.isRequired,
date: PropTypes.string.isRequired, date: PropTypes.string.isRequired,
+9 -23
View File
@@ -75,23 +75,13 @@ class Queue extends Component {
return; return;
} }
const nextState = {};
if (prevProps.items !== items) {
nextState.items = items;
}
const selectedIds = this.getSelectedIds(); const selectedIds = this.getSelectedIds();
const isPendingSelected = _.some(this.props.items, (item) => { const isPendingSelected = _.some(this.props.items, (item) => {
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay'; return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
}); });
if (isPendingSelected !== this.state.isPendingSelected) { if (isPendingSelected !== this.state.isPendingSelected) {
nextState.isPendingSelected = isPendingSelected; this.setState({ isPendingSelected });
}
if (!_.isEmpty(nextState)) {
this.setState(nextState);
} }
} }
@@ -224,29 +214,26 @@ class Queue extends Component {
<PageContentBody> <PageContentBody>
{ {
isRefreshing && !isAllPopulated ? isRefreshing && !isAllPopulated &&
<LoadingIndicator /> : <LoadingIndicator />
null
} }
{ {
!isRefreshing && hasError ? !isRefreshing && hasError &&
<div> <div>
{translate('FailedToLoadQueue')} {translate('FailedToLoadQueue')}
</div> : </div>
null
} }
{ {
isAllPopulated && !hasError && !items.length ? isAllPopulated && !hasError && !items.length &&
<div> <div>
{translate('QueueIsEmpty')} {translate('QueueIsEmpty')}
</div> : </div>
null
} }
{ {
isAllPopulated && !hasError && !!items.length ? isAllPopulated && !hasError && !!items.length &&
<div> <div>
<Table <Table
columns={columns} columns={columns}
@@ -281,8 +268,7 @@ class Queue extends Component {
isFetching={isRefreshing} isFetching={isRefreshing}
{...otherProps} {...otherProps}
/> />
</div> : </div>
null
} }
</PageContentBody> </PageContentBody>
-12
View File
@@ -128,7 +128,6 @@ class QueueRow extends Component {
{ {
columns.map((column) => { columns.map((column) => {
const { const {
name, name,
isVisible isVisible
@@ -235,16 +234,6 @@ class QueueRow extends Component {
); );
} }
if (name === 'year') {
return (
<TableRowCell key={name}>
{
movie ? movie.year : ''
}
</TableRowCell>
);
}
if (name === 'title') { if (name === 'title') {
return ( return (
<TableRowCell key={name}> <TableRowCell key={name}>
@@ -373,7 +362,6 @@ QueueRow.propTypes = {
estimatedCompletionTime: PropTypes.string, estimatedCompletionTime: PropTypes.string,
timeleft: PropTypes.string, timeleft: PropTypes.string,
size: PropTypes.number, size: PropTypes.number,
year: PropTypes.number,
sizeleft: PropTypes.number, sizeleft: PropTypes.number,
showRelativeDates: PropTypes.bool.isRequired, showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired, shortDateFormat: PropTypes.string.isRequired,
@@ -113,7 +113,6 @@ class AddNewMovieSearchResult extends Component {
images={images} images={images}
size={250} size={250}
overflow={true} overflow={true}
lazy={false}
/> />
</div> </div>
@@ -225,19 +225,13 @@ class ImportMovieFooter extends Component {
body={ body={
<ul> <ul>
{ {
Array.isArray(importError.responseJSON) ? importError.responseJSON.map((error, index) => {
importError.responseJSON.map((error, index) => { return (
return ( <li key={index}>
<li key={index}> {error.errorMessage}
{error.errorMessage} </li>
</li> );
); })
}) :
<li>
{
JSON.stringify(importError.responseJSON)
}
</li>
} }
</ul> </ul>
} }
@@ -152,19 +152,13 @@ class ImportMovieSelectFolder extends Component {
<ul> <ul>
{ {
Array.isArray(saveError.responseJSON) ? saveError.responseJSON.map((e, index) => {
saveError.responseJSON.map((e, index) => { return (
return ( <li key={index}>
<li key={index}> {e.errorMessage}
{e.errorMessage} </li>
</li> );
); })
}) :
<li>
{
JSON.stringify(saveError.responseJSON)
}
</li>
} }
</ul> </ul>
</Alert> : </Alert> :
@@ -162,7 +162,7 @@ class CalendarOptionsModalContent extends Component {
values={weekColumnOptions} values={weekColumnOptions}
value={calendarWeekColumnHeader} value={calendarWeekColumnHeader}
onChange={this.onGlobalInputChange} onChange={this.onGlobalInputChange}
helpText={translate('SettingsWeekColumnHeaderHelpText')} helpText={translate('HelpText')}
/> />
</FormGroup> </FormGroup>
@@ -113,12 +113,10 @@ class EnhancedSelectInput extends Component {
this._scheduleUpdate(); this._scheduleUpdate();
} }
if (!Array.isArray(this.props.value)) { if (!Array.isArray(this.props.value) && prevProps.value !== this.props.value) {
if (prevProps.value !== this.props.value || prevProps.values !== this.props.values) { this.setState({
this.setState({ selectedIndex: getSelectedIndex(this.props)
selectedIndex: getSelectedIndex(this.props) });
});
}
} }
} }
@@ -334,11 +332,6 @@ class EnhancedSelectInput extends Component {
const isMultiSelect = Array.isArray(value); const isMultiSelect = Array.isArray(value);
const selectedOption = getSelectedOption(selectedIndex, values); const selectedOption = getSelectedOption(selectedIndex, values);
let selectedValue = value;
if (!values.length) {
selectedValue = isMultiSelect ? [] : '';
}
return ( return (
<div> <div>
@@ -379,17 +372,15 @@ class EnhancedSelectInput extends Component {
onPress={this.onPress} onPress={this.onPress}
> >
{ {
isFetching ? isFetching &&
<LoadingIndicator <LoadingIndicator
className={styles.loading} className={styles.loading}
size={20} size={20}
/> : />
null
} }
{ {
isFetching ? !isFetching &&
null :
<Icon <Icon
name={icons.CARET_DOWN} name={icons.CARET_DOWN}
/> />
@@ -409,7 +400,7 @@ class EnhancedSelectInput extends Component {
onPress={this.onPress} onPress={this.onPress}
> >
<SelectedValueComponent <SelectedValueComponent
value={selectedValue} value={value}
values={values} values={values}
{...selectedValueOptions} {...selectedValueOptions}
{...selectedOption} {...selectedOption}
@@ -427,17 +418,15 @@ class EnhancedSelectInput extends Component {
> >
{ {
isFetching ? isFetching &&
<LoadingIndicator <LoadingIndicator
className={styles.loading} className={styles.loading}
size={20} size={20}
/> : />
null
} }
{ {
isFetching ? !isFetching &&
null :
<Icon <Icon
name={icons.CARET_DOWN} name={icons.CARET_DOWN}
/> />
@@ -517,7 +506,7 @@ class EnhancedSelectInput extends Component {
</Manager> </Manager>
{ {
isMobile ? isMobile &&
<Modal <Modal
className={styles.optionsModal} className={styles.optionsModal}
size={sizes.EXTRA_SMALL} size={sizes.EXTRA_SMALL}
@@ -568,8 +557,7 @@ class EnhancedSelectInput extends Component {
} }
</Scroller> </Scroller>
</ModalBody> </ModalBody>
</Modal> : </Modal>
null
} }
</div> </div>
); );
@@ -24,7 +24,7 @@ function HintedSelectInputSelectedValue(props) {
> >
<div className={styles.valueText}> <div className={styles.valueText}>
{ {
isMultiSelect ? isMultiSelect &&
value.map((key, index) => { value.map((key, index) => {
const v = valuesMap[key]; const v = valuesMap[key];
return ( return (
@@ -32,28 +32,26 @@ function HintedSelectInputSelectedValue(props) {
{v ? v.value : key} {v ? v.value : key}
</Label> </Label>
); );
}) : })
null
} }
{ {
isMultiSelect ? null : value !isMultiSelect && value
} }
</div> </div>
{ {
hint != null && includeHint ? hint != null && includeHint &&
<div className={styles.hintText}> <div className={styles.hintText}>
{hint} {hint}
</div> : </div>
null
} }
</EnhancedSelectInputSelectedValue> </EnhancedSelectInputSelectedValue>
); );
} }
HintedSelectInputSelectedValue.propTypes = { HintedSelectInputSelectedValue.propTypes = {
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))]).isRequired, value: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))]).isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired, values: PropTypes.arrayOf(PropTypes.object).isRequired,
hint: PropTypes.string, hint: PropTypes.string,
isMultiSelect: PropTypes.bool.isRequired, isMultiSelect: PropTypes.bool.isRequired,
@@ -68,7 +68,7 @@ RootFolderSelectInputOption.propTypes = {
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
freeSpace: PropTypes.number, freeSpace: PropTypes.number,
movieFolder: PropTypes.string, movieFolder: PropTypes.string,
isMissing: PropTypes.bool, isMissing: PropTypes.boolean,
isMobile: PropTypes.bool.isRequired, isMobile: PropTypes.bool.isRequired,
isWindows: PropTypes.bool isWindows: PropTypes.bool
}; };
@@ -112,12 +112,6 @@ class TextInput extends Component {
this._isMouseTarget = false; this._isMouseTarget = false;
}; };
onWheel = () => {
if (this.props.type === 'number') {
this._input.blur();
}
};
// //
// Render // Render
@@ -167,7 +161,6 @@ class TextInput extends Component {
onKeyUp={this.onKeyUp} onKeyUp={this.onKeyUp}
onMouseDown={this.onMouseDown} onMouseDown={this.onMouseDown}
onMouseUp={this.onMouseUp} onMouseUp={this.onMouseUp}
onWheel={this.onWheel}
/> />
); );
} }
+3 -4
View File
@@ -1,7 +1,6 @@
/* eslint-disable no-bitwise */ /* eslint-disable no-bitwise */
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import translate from 'Utilities/String/translate';
import EnhancedSelectInput from './EnhancedSelectInput'; import EnhancedSelectInput from './EnhancedSelectInput';
import styles from './UMaskInput.css'; import styles from './UMaskInput.css';
@@ -102,16 +101,16 @@ class UMaskInput extends Component {
</div> </div>
<div className={styles.details}> <div className={styles.details}>
<div> <div>
<label>{translate('UMask')}</label> <label>UMask</label>
<div className={styles.value}>{umask}</div> <div className={styles.value}>{umask}</div>
</div> </div>
<div> <div>
<label>{translate('Folder')}</label> <label>Folder</label>
<div className={styles.value}>{folder}</div> <div className={styles.value}>{folder}</div>
<div className={styles.unit}>d{formatPermissions(folderNum)}</div> <div className={styles.unit}>d{formatPermissions(folderNum)}</div>
</div> </div>
<div> <div>
<label>{translate('File')}</label> <label>File</label>
<div className={styles.value}>{file}</div> <div className={styles.value}>{file}</div>
<div className={styles.unit}>{formatPermissions(fileNum)}</div> <div className={styles.unit}>{formatPermissions(fileNum)}</div>
</div> </div>
+1 -1
View File
@@ -60,7 +60,7 @@ class FilterMenu extends Component {
iconName={icons.FILTER} iconName={icons.FILTER}
text={translate('Filter')} text={translate('Filter')}
isDisabled={isDisabled} isDisabled={isDisabled}
showIndicator={selectedFilterKey !== 'all'} indicator={selectedFilterKey !== 'all'}
/> />
<FilterMenuContent <FilterMenuContent
@@ -1,19 +1,11 @@
.menuButton { .menuButton {
composes: menuButton from '~./MenuButton.css'; composes: menuButton from '~./MenuButton.css';
position: relative;
&:hover { &:hover {
color: #666; color: #666;
} }
} }
.indicatorContainer {
position: absolute;
top: 10px;
left: 10px;
}
.label { .label {
margin-left: 5px; margin-left: 5px;
} }
+3 -21
View File
@@ -1,15 +1,13 @@
import classNames from 'classnames';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import MenuButton from 'Components/Menu/MenuButton'; import MenuButton from 'Components/Menu/MenuButton';
import { icons } from 'Helpers/Props';
import styles from './PageMenuButton.css'; import styles from './PageMenuButton.css';
function PageMenuButton(props) { function PageMenuButton(props) {
const { const {
iconName, iconName,
showIndicator, indicator,
text, text,
...otherProps ...otherProps
} = props; } = props;
@@ -24,22 +22,6 @@ function PageMenuButton(props) {
size={18} size={18}
/> />
{
showIndicator ?
<span
className={classNames(
styles.indicatorContainer,
'fa-layers fa-fw'
)}
>
<Icon
name={icons.CIRCLE}
size={9}
/>
</span> :
null
}
<div className={styles.label}> <div className={styles.label}>
{text} {text}
</div> </div>
@@ -50,11 +32,11 @@ function PageMenuButton(props) {
PageMenuButton.propTypes = { PageMenuButton.propTypes = {
iconName: PropTypes.object.isRequired, iconName: PropTypes.object.isRequired,
text: PropTypes.string, text: PropTypes.string,
showIndicator: PropTypes.bool.isRequired indicator: PropTypes.bool.isRequired
}; };
PageMenuButton.defaultProps = { PageMenuButton.defaultProps = {
showIndicator: false indicator: false
}; };
export default PageMenuButton; export default PageMenuButton;
@@ -9,7 +9,7 @@ import styles from './ToolbarMenuButton.css';
function ToolbarMenuButton(props) { function ToolbarMenuButton(props) {
const { const {
iconName, iconName,
showIndicator, indicator,
text, text,
...otherProps ...otherProps
} = props; } = props;
@@ -26,7 +26,7 @@ function ToolbarMenuButton(props) {
/> />
{ {
showIndicator && indicator &&
<span <span
className={classNames( className={classNames(
styles.indicatorContainer, styles.indicatorContainer,
@@ -53,11 +53,11 @@ function ToolbarMenuButton(props) {
ToolbarMenuButton.propTypes = { ToolbarMenuButton.propTypes = {
iconName: PropTypes.object.isRequired, iconName: PropTypes.object.isRequired,
text: PropTypes.string, text: PropTypes.string,
showIndicator: PropTypes.bool.isRequired indicator: PropTypes.bool.isRequired
}; };
ToolbarMenuButton.defaultProps = { ToolbarMenuButton.defaultProps = {
showIndicator: false indicator: false
}; };
export default ToolbarMenuButton; export default ToolbarMenuButton;
@@ -19,7 +19,7 @@
} }
} }
@media only screen and (max-width: $breakpointExtraLarge) { @media only screen and (max-width: $breakpointLarge) {
.contentFooter { .contentFooter {
flex-wrap: wrap; flex-wrap: wrap;
} }
+1 -5
View File
@@ -20,11 +20,7 @@
.frontTextContainer { .frontTextContainer {
z-index: 1; z-index: 1;
color: var(--progressBarFrontTextColor); color: var(--white);
}
.backTextContainer {
color: var(--progressBarBackTextColor);
} }
.backTextContainer, .backTextContainer,
@@ -73,7 +73,7 @@ function getInfoRowProps(row, props) {
return { return {
title: translate('Ratings'), title: translate('Ratings'),
iconName: icons.HEART, iconName: icons.HEART,
label: `${(props.ratings.tmdb.value * 10).toFixed()}%` label: `${props.ratings.tmdb.value * 10}%`
}; };
} }
@@ -47,10 +47,6 @@ export const possibleFilterTypes = {
{ key: filterTypes.CONTAINS, value: 'contains' }, { key: filterTypes.CONTAINS, value: 'contains' },
{ key: filterTypes.NOT_CONTAINS, value: 'does not contain' }, { key: filterTypes.NOT_CONTAINS, value: 'does not contain' },
{ key: filterTypes.EQUAL, value: 'equal' }, { key: filterTypes.EQUAL, value: 'equal' },
{ key: filterTypes.NOT_EQUAL, value: 'not equal' }, { key: filterTypes.NOT_EQUAL, value: 'not equal' }
{ key: filterTypes.STARTS_WITH, value: 'starts with' },
{ key: filterTypes.NOT_STARTS_WITH, value: 'does not start with' },
{ key: filterTypes.ENDS_WITH, value: 'ends with' },
{ key: filterTypes.NOT_ENDS_WITH, value: 'does not end with' }
] ]
}; };
@@ -39,22 +39,6 @@ const filterTypePredicates = {
[filterTypes.NOT_EQUAL]: function(itemValue, filterValue) { [filterTypes.NOT_EQUAL]: function(itemValue, filterValue) {
return itemValue !== filterValue; return itemValue !== filterValue;
},
[filterTypes.STARTS_WITH]: function(itemValue, filterValue) {
return itemValue.toLowerCase().startsWith(filterValue.toLowerCase());
},
[filterTypes.NOT_STARTS_WITH]: function(itemValue, filterValue) {
return !itemValue.toLowerCase().startsWith(filterValue.toLowerCase());
},
[filterTypes.ENDS_WITH]: function(itemValue, filterValue) {
return itemValue.toLowerCase().endsWith(filterValue.toLowerCase());
},
[filterTypes.NOT_ENDS_WITH]: function(itemValue, filterValue) {
return !itemValue.toLowerCase().endsWith(filterValue.toLowerCase());
} }
}; };
+1 -9
View File
@@ -10,10 +10,6 @@ export const LESS_THAN = 'lessThan';
export const LESS_THAN_OR_EQUAL = 'lessThanOrEqual'; export const LESS_THAN_OR_EQUAL = 'lessThanOrEqual';
export const NOT_CONTAINS = 'notContains'; export const NOT_CONTAINS = 'notContains';
export const NOT_EQUAL = 'notEqual'; export const NOT_EQUAL = 'notEqual';
export const STARTS_WITH = 'startsWith';
export const NOT_STARTS_WITH = 'notStartsWith';
export const ENDS_WITH = 'endsWith';
export const NOT_ENDS_WITH = 'notEndsWith';
export const all = [ export const all = [
CONTAINS, CONTAINS,
@@ -27,9 +23,5 @@ export const all = [
IN_LAST, IN_LAST,
NOT_IN_LAST, NOT_IN_LAST,
IN_NEXT, IN_NEXT,
NOT_IN_NEXT, NOT_IN_NEXT
STARTS_WITH,
NOT_STARTS_WITH,
ENDS_WITH,
NOT_ENDS_WITH
]; ];
@@ -64,15 +64,6 @@ const columns = [
isSortable: true, isSortable: true,
isVisible: true isVisible: true
}, },
{
name: 'customFormats',
label: React.createElement(Icon, {
name: icons.INTERACTIVE,
title: translate('CustomFormat')
}),
isSortable: true,
isVisible: true
},
{ {
name: 'rejections', name: 'rejections',
label: React.createElement(Icon, { label: React.createElement(Icon, {
@@ -5,10 +5,8 @@
} }
.quality, .quality,
.languages { .language {
composes: cell from '~Components/Table/Cells/TableRowCell.css'; composes: cell from '~Components/Table/Cells/TableRowCell.css';
text-align: center;
} }
.label { .label {
@@ -23,7 +21,3 @@
margin-top: 0; margin-top: 0;
text-align: start; text-align: start;
} }
.customFormatTooltip {
max-width: 250px;
}
@@ -12,7 +12,6 @@ import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal'
import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal'; import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal'; import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal'; import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
import MovieFormats from 'Movie/MovieFormats';
import MovieLanguage from 'Movie/MovieLanguage'; import MovieLanguage from 'Movie/MovieLanguage';
import MovieQuality from 'Movie/MovieQuality'; import MovieQuality from 'Movie/MovieQuality';
import formatBytes from 'Utilities/Number/formatBytes'; import formatBytes from 'Utilities/Number/formatBytes';
@@ -151,7 +150,6 @@ class InteractiveImportRow extends Component {
languages, languages,
releaseGroup, releaseGroup,
size, size,
customFormats,
rejections, rejections,
isReprocessing, isReprocessing,
isSelected, isSelected,
@@ -228,7 +226,7 @@ class InteractiveImportRow extends Component {
</TableRowCellButton> </TableRowCellButton>
<TableRowCellButton <TableRowCellButton
className={styles.languages} className={styles.language}
title={translate('ClickToChangeLanguage')} title={translate('ClickToChangeLanguage')}
onPress={this.onSelectLanguagePress} onPress={this.onSelectLanguagePress}
> >
@@ -261,26 +259,7 @@ class InteractiveImportRow extends Component {
<TableRowCell> <TableRowCell>
{ {
customFormats?.length ? !!rejections.length &&
<Popover
anchor={
<Icon name={icons.INTERACTIVE} />
}
title={translate('Formats')}
body={
<div className={styles.customFormatTooltip}>
<MovieFormats formats={customFormats} />
</div>
}
position={tooltipPositions.LEFT}
/> :
null
}
</TableRowCell>
<TableRowCell>
{
rejections.length ?
<Popover <Popover
anchor={ anchor={
<Icon <Icon
@@ -303,9 +282,7 @@ class InteractiveImportRow extends Component {
</ul> </ul>
} }
position={tooltipPositions.LEFT} position={tooltipPositions.LEFT}
canFlip={false} />
/> :
null
} }
</TableRowCell> </TableRowCell>
@@ -353,7 +330,6 @@ InteractiveImportRow.propTypes = {
languages: PropTypes.arrayOf(PropTypes.object), languages: PropTypes.arrayOf(PropTypes.object),
releaseGroup: PropTypes.string, releaseGroup: PropTypes.string,
size: PropTypes.number.isRequired, size: PropTypes.number.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),
rejections: PropTypes.arrayOf(PropTypes.object).isRequired, rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
isReprocessing: PropTypes.bool, isReprocessing: PropTypes.bool,
isSelected: PropTypes.bool, isSelected: PropTypes.bool,
@@ -62,7 +62,6 @@ class MovieHistoryRow extends Component {
sourceTitle, sourceTitle,
quality, quality,
customFormats, customFormats,
customFormatScore,
languages, languages,
qualityCutoffNotMet, qualityCutoffNotMet,
date, date,
@@ -107,7 +106,7 @@ class MovieHistoryRow extends Component {
</TableRowCell> </TableRowCell>
<TableRowCell key={name}> <TableRowCell key={name}>
{formatCustomFormatScore(customFormatScore)} {formatCustomFormatScore(data.customFormatScore)}
</TableRowCell> </TableRowCell>
<RelativeDateCellConnector <RelativeDateCellConnector
@@ -162,8 +161,7 @@ MovieHistoryRow.propTypes = {
sourceTitle: PropTypes.string.isRequired, sourceTitle: PropTypes.string.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired, languages: PropTypes.arrayOf(PropTypes.object).isRequired,
quality: PropTypes.object.isRequired, quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object), customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
customFormatScore: PropTypes.number.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired, qualityCutoffNotMet: PropTypes.bool.isRequired,
date: PropTypes.string.isRequired, date: PropTypes.string.isRequired,
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
@@ -17,10 +17,6 @@
font-size: 24px; font-size: 24px;
} }
.buttons {
flex: 0 0 auto;
}
.cloneButton { .cloneButton {
composes: button from '~Components/Link/IconButton.css'; composes: button from '~Components/Link/IconButton.css';
@@ -40,10 +36,3 @@
margin: 0; margin: 0;
border: none; border: none;
} }
.label {
@add-mixin truncate;
composes: label from '~Components/Label.css';
max-width: 100%;
}
@@ -90,7 +90,7 @@ class CustomFormat extends Component {
{name} {name}
</div> </div>
<div className={styles.buttons}> <div>
<IconButton <IconButton
className={styles.cloneButton} className={styles.cloneButton}
title={translate('CloneCustomFormat')} title={translate('CloneCustomFormat')}
@@ -124,7 +124,6 @@ class CustomFormat extends Component {
return ( return (
<Label <Label
className={styles.label}
key={index} key={index}
kind={kind} kind={kind}
> >
@@ -8,15 +8,11 @@
} }
.host { .host {
@add-mixin truncate; flex: 0 0 300px;
flex: 0 1 300px;
} }
.path { .path {
@add-mixin truncate; flex: 0 0 400px;
flex: 0 1 400px;
} }
.actions { .actions {
@@ -1,20 +1,15 @@
.remotePathMappingsHeader { .remotePathMappingsHeader {
display: flex; display: flex;
margin-bottom: 10px; margin-bottom: 10px;
padding-right: 24px;
font-weight: bold; font-weight: bold;
} }
.host { .host {
@add-mixin truncate; flex: 0 0 300px;
flex: 0 1 300px;
} }
.path { .path {
@add-mixin truncate; flex: 0 0 400px;
flex: 0 1 400px;
} }
.addRemotePathMapping { .addRemotePathMapping {
@@ -89,7 +89,6 @@ function EditIndexerModalContent(props) {
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="enableRss" name="enableRss"
helpText={supportsRss.value ? translate('RSSHelpText') : undefined}
helpTextWarning={supportsRss.value ? undefined : translate('RSSIsNotSupportedWithThisIndexer')} helpTextWarning={supportsRss.value ? undefined : translate('RSSIsNotSupportedWithThisIndexer')}
isDisabled={!supportsRss.value} isDisabled={!supportsRss.value}
{...enableRss} {...enableRss}
@@ -30,7 +30,6 @@
code { code {
padding: 0 1px; padding: 0 1px;
border: 1px solid var(--borderColor); border: 1px solid var(--borderColor);
background-color: var(--modalCloseButtonHoverColor); background-color: #f7f7f7;
color: var(--movieBackgroundColor);
} }
} }
@@ -7,11 +7,11 @@
&:hover { &:hover {
.token { .token {
background-color: var(--popoverTitleBackgroundInverseColor); background-color: #ddd;
} }
.example { .example {
background-color: var(--popoverTitleBorderInverseColor); background-color: #ccc;
} }
} }
} }
@@ -27,7 +27,7 @@
.token { .token {
flex: 0 0 50%; flex: 0 0 50%;
padding: 6px 16px; padding: 6px 16px;
background-color: var(--popoverTitleBorderColor); background-color: #eee;
font-family: $monoSpaceFontFamily; font-family: $monoSpaceFontFamily;
} }
@@ -38,7 +38,7 @@
justify-content: space-between; justify-content: space-between;
flex: 0 0 50%; flex: 0 0 50%;
padding: 6px 16px; padding: 6px 16px;
background-color: var(--popoverTitleBackgroundColor); background-color: #ddd;
.footNote { .footNote {
padding: 2px; padding: 2px;
@@ -18,7 +18,6 @@ export const certificationCountryOptions = [
{ key: 'fr', value: 'France' }, { key: 'fr', value: 'France' },
{ key: 'de', value: 'Germany' }, { key: 'de', value: 'Germany' },
{ key: 'gb', value: 'Great Britain' }, { key: 'gb', value: 'Great Britain' },
{ key: 'ie', value: 'Ireland' },
{ key: 'it', value: 'Italy' }, { key: 'it', value: 'Italy' },
{ key: 'es', value: 'Spain' }, { key: 'es', value: 'Spain' },
{ key: 'us', value: 'United States' }, { key: 'us', value: 'United States' },
@@ -191,7 +191,6 @@ const delayProfileShape = {
enableTorrent: PropTypes.shape(boolSettingShape).isRequired, enableTorrent: PropTypes.shape(boolSettingShape).isRequired,
usenetDelay: PropTypes.shape(numberSettingShape).isRequired, usenetDelay: PropTypes.shape(numberSettingShape).isRequired,
torrentDelay: PropTypes.shape(numberSettingShape).isRequired, torrentDelay: PropTypes.shape(numberSettingShape).isRequired,
bypassIfHighestQuality: PropTypes.shape(boolSettingShape).isRequired,
order: PropTypes.shape(numberSettingShape), order: PropTypes.shape(numberSettingShape),
tags: PropTypes.shape(tagSettingShape).isRequired tags: PropTypes.shape(tagSettingShape).isRequired
}; };
@@ -13,7 +13,6 @@ const newDelayProfile = {
preferredProtocol: 'usenet', preferredProtocol: 'usenet',
usenetDelay: 0, usenetDelay: 0,
torrentDelay: 0, torrentDelay: 0,
bypassIfHighestQuality: false,
tags: [] tags: []
}; };
+3 -3
View File
@@ -1,6 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { DndProvider } from 'react-dnd-multi-backend'; import { DndProvider } from 'react-dnd';
import HTML5toTouch from 'react-dnd-multi-backend/dist/esm/HTML5toTouch'; import { HTML5Backend } from 'react-dnd-html5-backend';
import Link from 'Components/Link/Link'; import Link from 'Components/Link/Link';
import PageContent from 'Components/Page/PageContent'; import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody'; import PageContentBody from 'Components/Page/PageContentBody';
@@ -25,7 +25,7 @@ class Profiles extends Component {
/> />
<PageContentBody> <PageContentBody>
<DndProvider options={HTML5toTouch}> <DndProvider backend={HTML5Backend}>
<QualityProfilesConnector /> <QualityProfilesConnector />
<DelayProfilesConnector /> <DelayProfilesConnector />
<div className={styles.addCustomFormatMessage}> <div className={styles.addCustomFormatMessage}>
@@ -118,12 +118,6 @@ export const defaultState = {
isSortable: true, isSortable: true,
isVisible: false isVisible: false
}, },
{
name: 'year',
label: translate('Year'),
isSortable: true,
isVisible: true
},
{ {
name: 'outputPath', name: 'outputPath',
label: translate('OutputPath'), label: translate('OutputPath'),
@@ -201,11 +201,6 @@ export const defaultState = {
return genreList.sort(sortByName); return genreList.sort(sortByName);
} }
}, },
{
name: 'customFormatScore',
label: translate('CustomFormatScore'),
type: filterBuilderTypes.NUMBER
},
{ {
name: 'rejectionCount', name: 'rejectionCount',
label: translate('RejectionCount'), label: translate('RejectionCount'),
-2
View File
@@ -226,8 +226,6 @@ module.exports = {
// //
// Misc // Misc
progressBarFrontTextColor: white,
progressBarBackTextColor: white,
progressBarBackgroundColor: '#727070', progressBarBackgroundColor: '#727070',
logEventsBackgroundColor: '#2a2a2a' logEventsBackgroundColor: '#2a2a2a'
}; };
-4
View File
@@ -1,11 +1,7 @@
import * as dark from './dark'; import * as dark from './dark';
import * as light from './light'; import * as light from './light';
const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const auto = defaultDark ? { ...dark } : { ...light };
export default { export default {
auto,
light, light,
dark dark
}; };
+4 -6
View File
@@ -198,8 +198,8 @@ module.exports = {
popoverShadowColor: 'rgba(0, 0, 0, 0.2)', popoverShadowColor: 'rgba(0, 0, 0, 0.2)',
popoverArrowBorderColor: '#fff', popoverArrowBorderColor: '#fff',
popoverTitleBackgroundInverseColor: '#9b9b9b', popoverTitleBackgroundInverseColor: '#595959',
popoverTitleBorderInverseColor: '#bfbfbf', popoverTitleBorderInverseColor: '#707070',
popoverShadowInverseColor: 'rgba(0, 0, 0, 0.2)', popoverShadowInverseColor: 'rgba(0, 0, 0, 0.2)',
popoverArrowBorderInverseColor: 'rgba(58, 63, 81, 0.75)', popoverArrowBorderInverseColor: 'rgba(58, 63, 81, 0.75)',
@@ -227,8 +227,6 @@ module.exports = {
// //
// Misc // Misc
progressBarFrontTextColor: white, progressBarBackgroundColor: '#fff',
progressBarBackTextColor: darkGray, logEventsBackgroundColor: '#fff'
progressBarBackgroundColor: white,
logEventsBackgroundColor: white
}; };
@@ -1,4 +1,5 @@
function formatCustomFormatScore(input, customFormatsLength = 0) {
function formatCustomFormatScore(input) {
const score = Number(input); const score = Number(input);
if (score > 0) { if (score > 0) {
@@ -9,7 +10,7 @@ function formatCustomFormatScore(input, customFormatsLength = 0) {
return score; return score;
} }
return customFormatsLength > 0 ? '+0' : ''; return '';
} }
export default formatCustomFormatScore; export default formatCustomFormatScore;
+6 -9
View File
@@ -8,8 +8,8 @@
"clean": "rimraf ./_output/UI && rimraf \"**/*.js.map\"", "clean": "rimraf ./_output/UI && rimraf \"**/*.js.map\"",
"start": "webpack --watch --config ./frontend/build/webpack.config.js", "start": "webpack --watch --config ./frontend/build/webpack.config.js",
"watch": "webpack --watch --config ./frontend/build/webpack.config.js", "watch": "webpack --watch --config ./frontend/build/webpack.config.js",
"lint": "eslint --config frontend/.eslintrc.js --ignore-path frontend/.eslintignore frontend/", "lint": "esprint check",
"lint-fix": "yarn lint --fix", "lint-fix": "esprint check --fix",
"stylelint-linux": "stylelint $(find frontend -name '*.css') --config frontend/.stylelintrc", "stylelint-linux": "stylelint $(find frontend -name '*.css') --config frontend/.stylelintrc",
"stylelint-windows": "stylelint frontend/**/*.css --config frontend/.stylelintrc" "stylelint-windows": "stylelint frontend/**/*.css --config frontend/.stylelintrc"
}, },
@@ -30,7 +30,7 @@
"@fortawesome/free-regular-svg-icons": "6.1.0", "@fortawesome/free-regular-svg-icons": "6.1.0",
"@fortawesome/free-solid-svg-icons": "6.1.0", "@fortawesome/free-solid-svg-icons": "6.1.0",
"@fortawesome/react-fontawesome": "0.1.18", "@fortawesome/react-fontawesome": "0.1.18",
"@microsoft/signalr": "6.0.16", "@microsoft/signalr": "6.0.8",
"@sentry/browser": "6.18.2", "@sentry/browser": "6.18.2",
"@sentry/integrations": "6.18.2", "@sentry/integrations": "6.18.2",
"classnames": "2.3.1", "classnames": "2.3.1",
@@ -108,11 +108,12 @@
"eslint-plugin-json": "3.1.0", "eslint-plugin-json": "3.1.0",
"eslint-plugin-react": "7.29.4", "eslint-plugin-react": "7.29.4",
"eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-simple-import-sort": "8.0.0", "eslint-plugin-simple-import-sort": "7.0.0",
"esprint": "3.3.0",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"filemanager-webpack-plugin": "5.0.0", "filemanager-webpack-plugin": "5.0.0",
"html-webpack-plugin": "5.3.1", "html-webpack-plugin": "5.3.1",
"loader-utils": "^3.2.1", "loader-utils": "^2.0.0",
"mini-css-extract-plugin": "1.5.0", "mini-css-extract-plugin": "1.5.0",
"postcss": "8.2.12", "postcss": "8.2.12",
"postcss-color-function": "4.1.0", "postcss-color-function": "4.1.0",
@@ -133,9 +134,5 @@
"webpack-cli": "4.9.1", "webpack-cli": "4.9.1",
"webpack-livereload-plugin": "3.0.2", "webpack-livereload-plugin": "3.0.2",
"worker-loader": "3.0.8" "worker-loader": "3.0.8"
},
"volta": {
"node": "16.17.0",
"yarn": "1.22.19"
} }
} }
-3
View File
@@ -1,3 +0,0 @@
is_global = true
dotnet_diagnostic.CA1014.severity = none
+1 -3
View File
@@ -1,9 +1,7 @@
<Project> <Project>
<!-- Common to all Radarr Projects --> <!-- Common to all Radarr Projects -->
<PropertyGroup> <PropertyGroup>
<AnalysisLevel>6.0-all</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles> <ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@@ -92,7 +90,7 @@
<!-- Standard testing packages --> <!-- Standard testing packages -->
<ItemGroup Condition="'$(TestProject)'=='true'"> <ItemGroup Condition="'$(TestProject)'=='true'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
<PackageReference Include="NUnit" Version="3.13.2" /> <PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Include="NunitXml.TestLogger" Version="3.0.117" /> <PackageReference Include="NunitXml.TestLogger" Version="3.0.117" />
@@ -1,25 +0,0 @@
using System.Globalization;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Test.ExtensionTests.StringExtensionTests
{
[TestFixture]
public class IsValidIPAddressFixture
{
[TestCase("192.168.0.1")]
[TestCase("::1")]
[TestCase("2001:db8:4006:812::200e")]
public void should_validate_ip_address(string input)
{
input.IsValidIpAddress().Should().BeTrue();
}
[TestCase("sonarr.tv")]
public void should_not_parse_non_ip_address(string input)
{
input.IsValidIpAddress().Should().BeFalse();
}
}
}
@@ -1,4 +1,4 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
@@ -10,7 +10,6 @@ namespace NzbDrone.Common.Test.Http
[TestCase("abc://my_host.com:8080/root/api/")] [TestCase("abc://my_host.com:8080/root/api/")]
[TestCase("abc://my_host.com:8080//root/api/")] [TestCase("abc://my_host.com:8080//root/api/")]
[TestCase("abc://my_host.com:8080/root//api/")] [TestCase("abc://my_host.com:8080/root//api/")]
[TestCase("abc://[::1]:8080/root//api/")]
public void should_parse(string uri) public void should_parse(string uri)
{ {
var newUri = new HttpUri(uri); var newUri = new HttpUri(uri);
+1 -1
View File
@@ -356,7 +356,7 @@ namespace NzbDrone.Common.Disk
} }
} }
public virtual string GetPathRoot(string path) public string GetPathRoot(string path)
{ {
Ensure.That(path, () => path).IsValidPath(); Ensure.That(path, () => path).IsValidPath();
@@ -11,7 +11,6 @@ namespace NzbDrone.Common.EnvironmentInfo
public interface IAppFolderFactory public interface IAppFolderFactory
{ {
void Register(); void Register();
void SetPermissions();
} }
public class AppFolderFactory : IAppFolderFactory public class AppFolderFactory : IAppFolderFactory
@@ -59,7 +58,7 @@ namespace NzbDrone.Common.EnvironmentInfo
InitializeMonoApplicationData(); InitializeMonoApplicationData();
} }
public void SetPermissions() private void SetPermissions()
{ {
try try
{ {
@@ -9,7 +9,6 @@ namespace NzbDrone.Common.EnvironmentInfo
bool IsAdmin { get; } bool IsAdmin { get; }
bool IsWindowsService { get; } bool IsWindowsService { get; }
bool IsWindowsTray { get; } bool IsWindowsTray { get; }
bool IsStarting { get; set; }
bool IsExiting { get; set; } bool IsExiting { get; set; }
bool IsTray { get; } bool IsTray { get; }
RuntimeMode Mode { get; } RuntimeMode Mode { get; }
@@ -19,7 +19,6 @@ namespace NzbDrone.Common.EnvironmentInfo
_logger = logger; _logger = logger;
IsWindowsService = hostLifetime is WindowsServiceLifetime; IsWindowsService = hostLifetime is WindowsServiceLifetime;
IsStarting = true;
// net6.0 will return Radarr.dll for entry assembly, we need the actual // net6.0 will return Radarr.dll for entry assembly, we need the actual
// executable name (Radarr on linux). On mono this will return the location of // executable name (Radarr on linux). On mono this will return the location of
@@ -83,7 +82,6 @@ namespace NzbDrone.Common.EnvironmentInfo
public bool IsWindowsService { get; private set; } public bool IsWindowsService { get; private set; }
public bool IsStarting { get; set; }
public bool IsExiting { get; set; } public bool IsExiting { get; set; }
public bool IsTray public bool IsTray
{ {
@@ -7,50 +7,34 @@ namespace NzbDrone.Common.Extensions
{ {
public static bool IsLocalAddress(this IPAddress ipAddress) public static bool IsLocalAddress(this IPAddress ipAddress)
{ {
// Map back to IPv4 if mapped to IPv6, for example "::ffff:1.2.3.4" to "1.2.3.4". if (ipAddress.IsIPv6LinkLocal)
if (ipAddress.IsIPv4MappedToIPv6)
{ {
ipAddress = ipAddress.MapToIPv4(); return true;
} }
// Checks loopback ranges for both IPv4 and IPv6.
if (IPAddress.IsLoopback(ipAddress)) if (IPAddress.IsLoopback(ipAddress))
{ {
return true; return true;
} }
// IPv4
if (ipAddress.AddressFamily == AddressFamily.InterNetwork) if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{ {
return IsLocalIPv4(ipAddress.GetAddressBytes()); byte[] bytes = ipAddress.GetAddressBytes();
} switch (bytes[0])
{
// IPv6 case 10:
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) case 127:
{ return true;
return ipAddress.IsIPv6LinkLocal || case 172:
ipAddress.IsIPv6UniqueLocal || return bytes[1] < 32 && bytes[1] >= 16;
ipAddress.IsIPv6SiteLocal; case 192:
return bytes[1] == 168;
default:
return false;
}
} }
return false; return false;
} }
private static bool IsLocalIPv4(byte[] ipv4Bytes)
{
// Link local (no IP assigned by DHCP): 169.254.0.0 to 169.254.255.255 (169.254.0.0/16)
bool IsLinkLocal() => ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254;
// Class A private range: 10.0.0.0 10.255.255.255 (10.0.0.0/8)
bool IsClassA() => ipv4Bytes[0] == 10;
// Class B private range: 172.16.0.0 172.31.255.255 (172.16.0.0/12)
bool IsClassB() => ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31;
// Class C private range: 192.168.0.0 192.168.255.255 (192.168.0.0/16)
bool IsClassC() => ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168;
return IsLinkLocal() || IsClassA() || IsClassC() || IsClassB();
}
} }
} }
@@ -2,8 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@@ -131,7 +129,7 @@ namespace NzbDrone.Common.Extensions
public static string WrapInQuotes(this string text) public static string WrapInQuotes(this string text)
{ {
if (!text.Contains(' ')) if (!text.Contains(" "))
{ {
return text; return text;
} }
@@ -194,30 +192,5 @@ namespace NzbDrone.Common.Extensions
.Replace("'", "%27") .Replace("'", "%27")
.Replace("%7E", "~"); .Replace("%7E", "~");
} }
public static bool IsValidIpAddress(this string value)
{
if (!IPAddress.TryParse(value, out var parsedAddress))
{
return false;
}
if (parsedAddress.Equals(IPAddress.Parse("255.255.255.255")))
{
return false;
}
if (parsedAddress.IsIPv6Multicast)
{
return false;
}
return parsedAddress.AddressFamily == AddressFamily.InterNetwork || parsedAddress.AddressFamily == AddressFamily.InterNetworkV6;
}
public static string ToUrlHost(this string input)
{
return input.Contains(':') ? $"[{input}]" : input;
}
} }
} }
@@ -216,7 +216,7 @@ namespace NzbDrone.Common.Http.Dispatchers
} }
} }
private static void AddContentHeader(HttpRequestMessage request, string header, string value) private void AddContentHeader(HttpRequestMessage request, string header, string value)
{ {
var headers = request.Content?.Headers; var headers = request.Content?.Headers;
if (headers == null) if (headers == null)
+3 -5
View File
@@ -8,7 +8,7 @@ namespace NzbDrone.Common.Http
{ {
public class HttpUri : IEquatable<HttpUri> public class HttpUri : IEquatable<HttpUri>
{ {
private static readonly Regex RegexUri = new Regex(@"^(?:(?<scheme>[a-z]+):)?(?://(?<host>[-_A-Z0-9.]+|\[[[A-F0-9:]+\])(?::(?<port>[0-9]{1,5}))?)?(?<path>(?:(?:(?<=^)|/+)[^/?#\r\n]+)+/*|/+)?(?:\?(?<query>[^#\r\n]*))?(?:\#(?<fragment>.*))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex RegexUri = new Regex(@"^(?:(?<scheme>[a-z]+):)?(?://(?<host>[-_A-Z0-9.]+)(?::(?<port>[0-9]{1,5}))?)?(?<path>(?:(?:(?<=^)|/+)[^/?#\r\n]+)+/*|/+)?(?:\?(?<query>[^#\r\n]*))?(?:\#(?<fragment>.*))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly string _uri; private readonly string _uri;
public string FullUri => _uri; public string FullUri => _uri;
@@ -70,8 +70,6 @@ namespace NzbDrone.Common.Http
private void Parse() private void Parse()
{ {
var parseSuccess = Uri.TryCreate(_uri, UriKind.RelativeOrAbsolute, out var uri);
var match = RegexUri.Match(_uri); var match = RegexUri.Match(_uri);
var scheme = match.Groups["scheme"]; var scheme = match.Groups["scheme"];
@@ -81,7 +79,7 @@ namespace NzbDrone.Common.Http
var query = match.Groups["query"]; var query = match.Groups["query"];
var fragment = match.Groups["fragment"]; var fragment = match.Groups["fragment"];
if (!parseSuccess || (scheme.Success && !host.Success && path.Success)) if (!match.Success || (scheme.Success && !host.Success && path.Success))
{ {
throw new ArgumentException("Uri didn't match expected pattern: " + _uri); throw new ArgumentException("Uri didn't match expected pattern: " + _uri);
} }
@@ -170,7 +168,7 @@ namespace NzbDrone.Common.Http
if (baseSlashIndex >= 0) if (baseSlashIndex >= 0)
{ {
return $"{basePath.AsSpan(0, baseSlashIndex)}/{relativePath}"; return basePath.Substring(0, baseSlashIndex) + "/" + relativePath;
} }
return relativePath; return relativePath;
@@ -11,41 +11,26 @@ namespace NzbDrone.Common.Instrumentation.Sentry
{ {
try try
{ {
if (sentryEvent.Message is not null) sentryEvent.Message.Message = CleanseLogMessage.Cleanse(sentryEvent.Message.Message);
{
sentryEvent.Message.Formatted = CleanseLogMessage.Cleanse(sentryEvent.Message.Formatted);
sentryEvent.Message.Message = CleanseLogMessage.Cleanse(sentryEvent.Message.Message);
sentryEvent.Message.Params = sentryEvent.Message.Params?.Select(x => CleanseLogMessage.Cleanse(x switch
{
string str => str,
_ => x.ToString()
})).ToList();
}
if (sentryEvent.Fingerprint.Any()) if (sentryEvent.Fingerprint != null)
{ {
var fingerprint = sentryEvent.Fingerprint.Select(x => CleanseLogMessage.Cleanse(x)).ToList(); var fingerprint = sentryEvent.Fingerprint.Select(x => CleanseLogMessage.Cleanse(x)).ToList();
sentryEvent.SetFingerprint(fingerprint); sentryEvent.SetFingerprint(fingerprint);
} }
if (sentryEvent.Extra.Any()) if (sentryEvent.Extra != null)
{ {
var extras = sentryEvent.Extra.ToDictionary(x => x.Key, y => (object)CleanseLogMessage.Cleanse(y.Value as string)); var extras = sentryEvent.Extra.ToDictionary(x => x.Key, y => (object)CleanseLogMessage.Cleanse((string)y.Value));
sentryEvent.SetExtras(extras); sentryEvent.SetExtras(extras);
} }
if (sentryEvent.SentryExceptions is not null) foreach (var exception in sentryEvent.SentryExceptions)
{ {
foreach (var exception in sentryEvent.SentryExceptions) exception.Value = CleanseLogMessage.Cleanse(exception.Value);
foreach (var frame in exception.Stacktrace.Frames)
{ {
exception.Value = CleanseLogMessage.Cleanse(exception.Value); frame.FileName = ShortenPath(frame.FileName);
if (exception.Stacktrace is not null)
{
foreach (var frame in exception.Stacktrace.Frames)
{
frame.FileName = ShortenPath(frame.FileName);
}
}
} }
} }
} }
@@ -8,7 +8,6 @@ using System.Threading;
using NLog; using NLog;
using NLog.Common; using NLog.Common;
using NLog.Targets; using NLog.Targets;
using Npgsql;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using Sentry; using Sentry;
@@ -35,14 +34,6 @@ namespace NzbDrone.Common.Instrumentation.Sentry
SQLiteErrorCode.Auth SQLiteErrorCode.Auth
}; };
private static readonly HashSet<string> FilteredPostgresErrorCodes = new HashSet<string>
{
PostgresErrorCodes.OutOfMemory,
PostgresErrorCodes.TooManyConnections,
PostgresErrorCodes.DiskFull,
PostgresErrorCodes.ProgramLimitExceeded
};
// use string and not Type so we don't need a reference to the project // use string and not Type so we don't need a reference to the project
// where these are defined // where these are defined
private static readonly HashSet<string> FilteredExceptionTypeNames = new HashSet<string> private static readonly HashSet<string> FilteredExceptionTypeNames = new HashSet<string>
@@ -108,6 +99,9 @@ namespace NzbDrone.Common.Instrumentation.Sentry
o.Dsn = dsn; o.Dsn = dsn;
o.AttachStacktrace = true; o.AttachStacktrace = true;
o.MaxBreadcrumbs = 200; o.MaxBreadcrumbs = 200;
o.SendDefaultPii = false;
o.Debug = false;
o.DiagnosticLevel = SentryLevel.Debug;
o.Release = BuildInfo.Release; o.Release = BuildInfo.Release;
o.BeforeSend = x => SentryCleanser.CleanseEvent(x); o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x); o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
@@ -259,19 +253,6 @@ namespace NzbDrone.Common.Instrumentation.Sentry
isSentry = false; isSentry = false;
} }
var pgEx = logEvent.Exception as PostgresException;
if (pgEx != null && FilteredPostgresErrorCodes.Contains(pgEx.SqlState))
{
return false;
}
// We don't care about transient network and timeout errors
var npgEx = logEvent.Exception as NpgsqlException;
if (npgEx != null && npgEx.IsTransient)
{
return false;
}
if (FilteredExceptionTypeNames.Contains(ex.GetType().Name)) if (FilteredExceptionTypeNames.Contains(ex.GetType().Name))
{ {
isSentry = false; isSentry = false;
+6 -7
View File
@@ -5,19 +5,18 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DryIoc.dll" Version="5.3.0" /> <PackageReference Include="DryIoc.dll" Version="5.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NLog" Version="5.0.1" /> <PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" /> <PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
<PackageReference Include="Npgsql" Version="5.0.11" /> <PackageReference Include="Sentry" Version="3.20.1" />
<PackageReference Include="Sentry" Version="3.23.1" />
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" /> <PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
<PackageReference Include="SharpZipLib" Version="1.3.3" /> <PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="System.Text.Json" Version="6.0.7" /> <PackageReference Include="System.Text.Json" Version="6.0.5" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" /> <PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" /> <PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" /> <PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" /> <PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="6.0.0" /> <PackageReference Include="System.ServiceProcess.ServiceController" Version="6.0.0" />
+1 -1
View File
@@ -64,7 +64,7 @@ namespace NzbDrone.Common
var args = $"create {serviceName} " + var args = $"create {serviceName} " +
$"DisplayName= \"{serviceName}\" " + $"DisplayName= \"{serviceName}\" " +
$"binpath= \"{Environment.ProcessPath}\" " + $"binpath= \"{Process.GetCurrentProcess().MainModule.FileName}\" " +
"start= auto " + "start= auto " +
"depend= EventLog/Tcpip/http " + "depend= EventLog/Tcpip/http " +
"obj= \"NT AUTHORITY\\LocalService\""; "obj= \"NT AUTHORITY\\LocalService\"";
@@ -19,7 +19,7 @@ namespace NzbDrone.Common.TPL
private readonly int _maxDegreeOfParallelism; private readonly int _maxDegreeOfParallelism;
/// <summary>Whether the scheduler is currently processing work items.</summary> /// <summary>Whether the scheduler is currently processing work items.</summary>
private int _delegatesQueuedOrRunning; private int _delegatesQueuedOrRunning = 0;
/// <summary> /// <summary>
/// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the /// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the
@@ -81,9 +81,8 @@ namespace NzbDrone.Core.Test.Blocklisting
Subject.DeleteForMovies(new List<int> { _movie1.Id }); Subject.DeleteForMovies(new List<int> { _movie1.Id });
var blocklist = Subject.All(); var removedMovieBlocklists = Subject.BlocklistedByMovie(_movie1.Id);
var removedMovieBlocklists = blocklist.Where(b => b.MovieId == _movie1.Id); var nonRemovedMovieBlocklists = Subject.BlocklistedByMovie(_movie2.Id);
var nonRemovedMovieBlocklists = blocklist.Where(b => b.MovieId == _movie2.Id);
removedMovieBlocklists.Should().HaveCount(0); removedMovieBlocklists.Should().HaveCount(0);
nonRemovedMovieBlocklists.Should().HaveCount(1); nonRemovedMovieBlocklists.Should().HaveCount(1);
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
@@ -9,7 +9,7 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.CustomFormats namespace NzbDrone.Core.Test.CustomFormats
{ {
[TestFixture] [TestFixture]
public class CustomFormatsTestHelpers : CoreTest public class CustomFormatsFixture : CoreTest
{ {
private static List<CustomFormat> _customFormats { get; set; } private static List<CustomFormat> _customFormats { get; set; }
@@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.Datastore.Migration
[TestFixture] [TestFixture]
public class custom_formatsFixture : MigrationTest<add_custom_formats> public class custom_formatsFixture : MigrationTest<add_custom_formats>
{ {
public static Dictionary<int, int> QualityToDefinition; public static Dictionary<int, int> QualityToDefinition = null;
public void AddDefaultProfile(add_custom_formats m, string name, Quality cutoff, params Quality[] allowed) public void AddDefaultProfile(add_custom_formats m, string name, Quality cutoff, params Quality[] allowed)
{ {
@@ -46,14 +46,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
ParsedMovieInfo = new ParsedMovieInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, ParsedMovieInfo = new ParsedMovieInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
}; };
CustomFormatsTestHelpers.GivenCustomFormats(_format1, _format2); CustomFormatsFixture.GivenCustomFormats(_format1, _format2);
} }
[Test] [Test]
public void should_allow_if_format_score_greater_than_min() public void should_allow_if_format_score_greater_than_min()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { _format1 }; _remoteMovie.CustomFormats = new List<CustomFormat> { _format1 };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_deny_if_format_score_not_greater_than_min() public void should_deny_if_format_score_not_greater_than_min()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { _format2 }; _remoteMovie.CustomFormats = new List<CustomFormat> { _format2 };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
Console.WriteLine(_remoteMovie.CustomFormatScore); Console.WriteLine(_remoteMovie.CustomFormatScore);
@@ -76,7 +76,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_deny_if_format_score_not_greater_than_min_2() public void should_deny_if_format_score_not_greater_than_min_2()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { _format2, _format1 }; _remoteMovie.CustomFormats = new List<CustomFormat> { _format2, _format1 };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
@@ -86,7 +86,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_allow_if_all_format_is_defined_in_profile() public void should_allow_if_all_format_is_defined_in_profile()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { _format2, _format1 }; _remoteMovie.CustomFormats = new List<CustomFormat> { _format2, _format1 };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name);
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
@@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_deny_if_no_format_was_parsed_and_min_score_positive() public void should_deny_if_no_format_was_parsed_and_min_score_positive()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { }; _remoteMovie.CustomFormats = new List<CustomFormat> { };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name);
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
@@ -106,7 +106,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_allow_if_no_format_was_parsed_min_score_is_zero() public void should_allow_if_no_format_was_parsed_min_score_is_zero()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { }; _remoteMovie.CustomFormats = new List<CustomFormat> { };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name);
_remoteMovie.Movie.Profile.MinFormatScore = 0; _remoteMovie.Movie.Profile.MinFormatScore = 0;
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
@@ -40,8 +40,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private void GivenProfile(Profile profile) private void GivenProfile(Profile profile)
{ {
CustomFormatsTestHelpers.GivenCustomFormats(); CustomFormatsFixture.GivenCustomFormats();
profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(); profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems();
profile.MinFormatScore = 0; profile.MinFormatScore = 0;
_remoteMovie.Movie.Profile = profile; _remoteMovie.Movie.Profile = profile;
@@ -74,7 +74,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
_customFormat = new CustomFormat("My Format", new ResolutionSpecification { Value = (int)Resolution.R1080p }) { Id = 1 }; _customFormat = new CustomFormat("My Format", new ResolutionSpecification { Value = (int)Resolution.R1080p }) { Id = 1 };
CustomFormatsTestHelpers.GivenCustomFormats(_customFormat); CustomFormatsFixture.GivenCustomFormats(_customFormat);
} }
[Test] [Test]
@@ -157,7 +157,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Cutoff = Quality.HDTV720p.Id, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
MinFormatScore = 0, MinFormatScore = 0,
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("My Format"), FormatItems = CustomFormatsFixture.GetSampleFormatItems("My Format"),
UpgradeAllowed = true UpgradeAllowed = true
}); });
@@ -38,14 +38,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.Resolve<UpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
_upgradeHistory = Mocker.Resolve<HistorySpecification>(); _upgradeHistory = Mocker.Resolve<HistorySpecification>();
CustomFormatsTestHelpers.GivenCustomFormats(); CustomFormatsFixture.GivenCustomFormats();
_fakeMovie = Builder<Movie>.CreateNew() _fakeMovie = Builder<Movie>.CreateNew()
.With(c => c.Profile = new Profile .With(c => c.Profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
Cutoff = Quality.Bluray1080p.Id, Cutoff = Quality.Bluray1080p.Id,
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("None"), FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
MinFormatScore = 0, MinFormatScore = 0,
UpgradeAllowed = true UpgradeAllowed = true
}) })
@@ -66,7 +66,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Returns(true); .Returns(true);
Mocker.GetMock<ICustomFormatCalculationService>() Mocker.GetMock<ICustomFormatCalculationService>()
.Setup(x => x.ParseCustomFormat(It.IsAny<MovieHistory>(), It.IsAny<Movie>())) .Setup(x => x.ParseCustomFormat(It.IsAny<MovieHistory>()))
.Returns(new List<CustomFormat>()); .Returns(new List<CustomFormat>());
} }
@@ -163,7 +163,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
Cutoff = Quality.Bluray1080p.Id, Cutoff = Quality.Bluray1080p.Id,
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(), FormatItems = CustomFormatsFixture.GetSampleFormatItems(),
MinFormatScore = 0 MinFormatScore = 0
}; };
@@ -171,7 +171,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); _upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
Mocker.GetMock<ICustomFormatCalculationService>() Mocker.GetMock<ICustomFormatCalculationService>()
.Setup(x => x.ParseCustomFormat(It.IsAny<MovieHistory>(), It.IsAny<Movie>())) .Setup(x => x.ParseCustomFormat(It.IsAny<MovieHistory>()))
.Returns(new List<CustomFormat>()); .Returns(new List<CustomFormat>());
GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, MovieHistoryEventType.Grabbed); GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, MovieHistoryEventType.Grabbed);
@@ -186,7 +186,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
Cutoff = Quality.WEBDL1080p.Id, Cutoff = Quality.WEBDL1080p.Id,
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(), FormatItems = CustomFormatsFixture.GetSampleFormatItems(),
MinFormatScore = 0 MinFormatScore = 0
}; };
@@ -221,7 +221,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
Cutoff = Quality.WEBDL1080p.Id, Cutoff = Quality.WEBDL1080p.Id,
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(), FormatItems = CustomFormatsFixture.GetSampleFormatItems(),
MinFormatScore = 0 MinFormatScore = 0
}; };
@@ -41,17 +41,17 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private void WithEnglishRelease() private void WithEnglishRelease()
{ {
_remoteMovie.Languages = new List<Language> { Language.English }; _remoteMovie.ParsedMovieInfo.Languages = new List<Language> { Language.English };
} }
private void WithGermanRelease() private void WithGermanRelease()
{ {
_remoteMovie.Languages = new List<Language> { Language.German }; _remoteMovie.ParsedMovieInfo.Languages = new List<Language> { Language.German };
} }
private void WithFrenchRelease() private void WithFrenchRelease()
{ {
_remoteMovie.Languages = new List<Language> { Language.French }; _remoteMovie.ParsedMovieInfo.Languages = new List<Language> { Language.French };
} }
[Test] [Test]
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_customFormat1 = new CustomFormat("My Format 1", new LanguageSpecification { Value = (int)Language.English }) { Id = 1 }; _customFormat1 = new CustomFormat("My Format 1", new LanguageSpecification { Value = (int)Language.English }) { Id = 1 };
_customFormat2 = new CustomFormat("My Format 2", new LanguageSpecification { Value = (int)Language.French }) { Id = 2 }; _customFormat2 = new CustomFormat("My Format 2", new LanguageSpecification { Value = (int)Language.French }) { Id = 2 };
CustomFormatsTestHelpers.GivenCustomFormats(_customFormat1, _customFormat2); CustomFormatsFixture.GivenCustomFormats(_customFormat1, _customFormat2);
Mocker.GetMock<IQualityDefinitionService>() Mocker.GetMock<IQualityDefinitionService>()
.Setup(s => s.Get(It.IsAny<Quality>())) .Setup(s => s.Get(It.IsAny<Quality>()))
@@ -62,7 +62,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
remoteMovie.Movie = Builder<Movie>.CreateNew().With(m => m.Profile = new Profile remoteMovie.Movie = Builder<Movie>.CreateNew().With(m => m.Profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name), FormatItems = CustomFormatsFixture.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name),
MinFormatScore = 0 MinFormatScore = 0
}) })
.With(m => m.Title = "A Movie") .With(m => m.Title = "A Movie")
@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
CustomFormatsTestHelpers.GivenCustomFormats(_customFormat1, _customFormat2); CustomFormatsFixture.GivenCustomFormats(_customFormat1, _customFormat2);
} }
private void GivenAutoDownloadPropers(ProperDownloadTypes type) private void GivenAutoDownloadPropers(ProperDownloadTypes type)
@@ -73,7 +73,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var profile = new Profile var profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name), FormatItems = CustomFormatsFixture.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name),
MinFormatScore = 0 MinFormatScore = 0
}; };
@@ -4,11 +4,9 @@ using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.CustomFormats; using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles;
@@ -34,13 +32,13 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
Mocker.Resolve<UpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
CustomFormatsTestHelpers.GivenCustomFormats(); CustomFormatsFixture.GivenCustomFormats();
_movie = Builder<Movie>.CreateNew() _movie = Builder<Movie>.CreateNew()
.With(e => e.Profile = new Profile .With(e => e.Profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(), FormatItems = CustomFormatsFixture.GetSampleFormatItems(),
MinFormatScore = 0, MinFormatScore = 0,
UpgradeAllowed = true UpgradeAllowed = true
}) })
@@ -60,7 +58,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Build(); .Build();
Mocker.GetMock<ICustomFormatCalculationService>() Mocker.GetMock<ICustomFormatCalculationService>()
.Setup(x => x.ParseCustomFormat(It.IsAny<RemoteMovie>(), It.IsAny<long>())) .Setup(x => x.ParseCustomFormat(It.IsAny<ParsedMovieInfo>(), _movie))
.Returns(new List<CustomFormat>()); .Returns(new List<CustomFormat>());
} }
@@ -206,31 +204,5 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
} }
[Test]
public void should_return_false_if_same_quality_non_proper_in_queue_and_download_propers_is_do_not_upgrade()
{
_remoteMovie.ParsedMovieInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(2));
_movie.Profile.Cutoff = _remoteMovie.ParsedMovieInfo.Quality.Quality.Id;
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotUpgrade);
var remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(r => r.Movie = _movie)
.With(r => r.ParsedMovieInfo = new ParsedMovieInfo
{
Quality = new QualityModel(Quality.HDTV720p),
Languages = new List<Language> { Language.English }
})
.With(r => r.Release = _releaseInfo)
.With(r => r.CustomFormats = new List<CustomFormat>())
.Build();
GivenQueue(new List<RemoteMovie> { remoteMovie });
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
}
} }
} }
@@ -1,209 +0,0 @@
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
[TestFixture]
public class UpgradeAllowedSpecificationFixture : CoreTest<UpgradableSpecification>
{
private CustomFormat _customFormatOne;
private CustomFormat _customFormatTwo;
private Profile _qualityProfile;
[SetUp]
public void Setup()
{
_customFormatOne = new CustomFormat
{
Id = 1,
Name = "One"
};
_customFormatTwo = new CustomFormat
{
Id = 2,
Name = "Two"
};
_qualityProfile = new Profile
{
Cutoff = Quality.Bluray1080p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(),
UpgradeAllowed = false,
CutoffFormatScore = 100,
FormatItems = new List<ProfileFormatItem>
{
new ProfileFormatItem
{
Format = _customFormatOne,
Score = 50
},
new ProfileFormatItem
{
Format = _customFormatTwo,
Score = 100
}
}
};
}
[Test]
public void should_return_false_when_quality_is_better_custom_formats_are_the_same_and_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.Bluray1080p),
new List<CustomFormat>())
.Should().BeFalse();
}
[Test]
public void should_return_false_when_quality_is_same_and_custom_format_is_upgrade_and_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne },
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatTwo })
.Should().BeFalse();
}
[Test]
public void should_return_true_for_custom_format_upgrade_when_upgrading_is_allowed()
{
_qualityProfile.UpgradeAllowed = true;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne },
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatTwo })
.Should().BeTrue();
}
[Test]
public void should_return_true_for_same_custom_format_score_when_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne },
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne })
.Should().BeTrue();
}
[Test]
public void should_return_true_for_lower_custom_format_score_when_upgrading_is_allowed()
{
_qualityProfile.UpgradeAllowed = true;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatTwo },
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne })
.Should().BeTrue();
}
[Test]
public void should_return_true_for_lower_language_when_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatTwo },
new QualityModel(Quality.DVD),
new List<CustomFormat> { _customFormatOne })
.Should().BeTrue();
}
[Test]
public void should_return_true_for_quality_upgrade_when_upgrading_is_allowed()
{
_qualityProfile.UpgradeAllowed = true;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.Bluray1080p),
new List<CustomFormat>())
.Should().BeTrue();
}
[Test]
public void should_return_true_for_same_quality_when_upgrading_is_allowed()
{
_qualityProfile.UpgradeAllowed = true;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.DVD),
new List<CustomFormat>())
.Should().BeTrue();
}
[Test]
public void should_return_true_for_same_quality_when_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.DVD),
new List<CustomFormat>())
.Should().BeTrue();
}
[Test]
public void should_return_true_for_lower_quality_when_upgrading_is_allowed()
{
_qualityProfile.UpgradeAllowed = true;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.SDTV),
new List<CustomFormat>())
.Should().BeTrue();
}
[Test]
public void should_return_true_for_lower_quality_when_upgrading_is_not_allowed()
{
_qualityProfile.UpgradeAllowed = false;
Subject.IsUpgradeAllowed(
_qualityProfile,
new QualityModel(Quality.DVD),
new List<CustomFormat>(),
new QualityModel(Quality.SDTV),
new List<CustomFormat>())
.Should().BeTrue();
}
}
}
@@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.Resolve<UpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
_upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>(); _upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>();
CustomFormatsTestHelpers.GivenCustomFormats(); CustomFormatsFixture.GivenCustomFormats();
_firstFile = new MovieFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now }; _firstFile = new MovieFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now };
@@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.With(c => c.Profile = new Profile .With(c => c.Profile = new Profile
{ {
Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities(), Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities(),
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(), FormatItems = CustomFormatsFixture.GetSampleFormatItems(),
MinFormatScore = 0 MinFormatScore = 0
}) })
.With(e => e.MovieFile = _firstFile) .With(e => e.MovieFile = _firstFile)
@@ -1,112 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Download.Aggregation.Aggregators;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download.Aggregation.Aggregators
{
[TestFixture]
public class AggregateLanguagesFixture : CoreTest<AggregateLanguages>
{
private RemoteMovie _remoteMovie;
private Movie _movie;
private string _simpleReleaseTitle = "Series.Title.S01E01.xyz-RlsGroup";
[SetUp]
public void Setup()
{
_movie = Builder<Movie>.CreateNew()
.With(m => m.MovieMetadata = new MovieMetadata
{
Title = "Some Movie",
OriginalLanguage = Language.English
})
.Build();
_remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(l => l.ParsedMovieInfo = null)
.With(l => l.Movie = _movie)
.Build();
}
private ParsedMovieInfo GetParsedMovieInfo(List<Language> languages, string releaseTitle, string releaseTokens = "")
{
return new ParsedMovieInfo
{
Languages = languages,
ReleaseTitle = releaseTitle,
SimpleReleaseTitle = releaseTokens
};
}
[Test]
public void should_return_existing_language_if_episode_title_does_not_have_language()
{
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.Original }, _simpleReleaseTitle);
Subject.Aggregate(_remoteMovie).Languages.Should().Contain(_movie.MovieMetadata.Value.OriginalLanguage);
}
[Test]
public void should_return_parsed_language()
{
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.French }, _simpleReleaseTitle);
Subject.Aggregate(_remoteMovie).Languages.Should().Equal(_remoteMovie.ParsedMovieInfo.Languages);
}
[Test]
public void should_exclude_language_that_is_part_of_episode_title_when_release_tokens_contains_episode_title()
{
var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.xyz-RlsGroup";
var releaseTokens = ".Jimmy.The.Greek.xyz-RlsGroup";
_remoteMovie.Movie.Title = "Jimmy The Greek";
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.Greek }, releaseTitle, releaseTokens);
Subject.Aggregate(_remoteMovie).Languages.Should().Equal(_movie.MovieMetadata.Value.OriginalLanguage);
}
[Test]
public void should_remove_parsed_language_that_is_part_of_episode_title_when_release_tokens_contains_episode_title()
{
var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.French.xyz-RlsGroup";
var releaseTokens = ".Jimmy.The.Greek.French.xyz-RlsGroup";
_remoteMovie.Movie.Title = "Jimmy The Greek";
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.Greek, Language.French }, releaseTitle, releaseTokens);
Subject.Aggregate(_remoteMovie).Languages.Should().Equal(Language.French);
}
[Test]
public void should_not_exclude_language_that_is_part_of_episode_title_when_release_tokens_does_not_contain_episode_title()
{
var releaseTitle = "Series.Title.S01E01.xyz-RlsGroup";
var releaseTokens = ".xyz-RlsGroup";
_remoteMovie.Movie.Title = "Jimmy The Greek";
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.Greek }, releaseTitle, releaseTokens);
Subject.Aggregate(_remoteMovie).Languages.Should().Equal(Language.Greek);
}
[Test]
public void should_use_reparse_language_after_determining_languages_that_are_in_episode_titles()
{
var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.Greek.xyz-RlsGroup";
var releaseTokens = ".Jimmy.The.Greek.Greek.xyz-RlsGroup";
_remoteMovie.Movie.Title = "Jimmy The Greek";
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.Greek }, releaseTitle, releaseTokens);
Subject.Aggregate(_remoteMovie).Languages.Should().Equal(Language.Greek);
}
}
}
@@ -1,367 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Download.Clients.FreeboxDownload;
using NzbDrone.Core.Download.Clients.FreeboxDownload.Responses;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.FreeboxDownloadTests
{
[TestFixture]
public class TorrentFreeboxDownloadFixture : DownloadClientFixtureBase<TorrentFreeboxDownload>
{
protected FreeboxDownloadSettings _settings;
protected FreeboxDownloadConfiguration _downloadConfiguration;
protected FreeboxDownloadTask _task;
protected string _defaultDestination = @"/some/path";
protected string _encodedDefaultDestination = "L3NvbWUvcGF0aA==";
protected string _category = "somecat";
protected string _encodedDefaultDestinationAndCategory = "L3NvbWUvcGF0aC9zb21lY2F0";
protected string _destinationDirectory = @"/path/to/media";
protected string _encodedDestinationDirectory = "L3BhdGgvdG8vbWVkaWE=";
protected OsPath _physicalPath = new OsPath("/mnt/sdb1/mydata");
protected string _downloadURL => "magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcad53426&dn=download";
[SetUp]
public void Setup()
{
Subject.Definition = new DownloadClientDefinition();
_settings = new FreeboxDownloadSettings()
{
Host = "127.0.0.1",
Port = 443,
ApiUrl = "/api/v1/",
AppId = "someid",
AppToken = "S0mEv3RY1oN9T0k3n"
};
Subject.Definition.Settings = _settings;
_downloadConfiguration = new FreeboxDownloadConfiguration()
{
DownloadDirectory = _encodedDefaultDestination
};
_task = new FreeboxDownloadTask()
{
Id = "id0",
Name = "name",
DownloadDirectory = "L3NvbWUvcGF0aA==",
InfoHash = "HASH",
QueuePosition = 1,
Status = FreeboxDownloadTaskStatus.Unknown,
Eta = 0,
Error = "none",
Type = FreeboxDownloadTaskType.Bt.ToString(),
IoPriority = FreeboxDownloadTaskIoPriority.Normal.ToString(),
StopRatio = 150,
PieceLength = 125,
CreatedTimestamp = 1665261599,
Size = 1000,
ReceivedPrct = 0,
ReceivedBytes = 0,
ReceivedRate = 0,
TransmittedPrct = 0,
TransmittedBytes = 0,
TransmittedRate = 0,
};
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>()));
}
protected void GivenCategory()
{
_settings.Category = _category;
}
protected void GivenDestinationDirectory()
{
_settings.DestinationDirectory = _destinationDirectory;
}
protected virtual void GivenDownloadConfiguration()
{
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.GetDownloadConfiguration(It.IsAny<FreeboxDownloadSettings>()))
.Returns(_downloadConfiguration);
}
protected virtual void GivenTasks(List<FreeboxDownloadTask> torrents)
{
if (torrents == null)
{
torrents = new List<FreeboxDownloadTask>();
}
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.GetTasks(It.IsAny<FreeboxDownloadSettings>()))
.Returns(torrents);
}
protected void PrepareClientToReturnQueuedItem()
{
_task.Status = FreeboxDownloadTaskStatus.Queued;
GivenTasks(new List<FreeboxDownloadTask>
{
_task
});
}
protected void GivenSuccessfulDownload()
{
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[1000]));
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()))
.Callback(PrepareClientToReturnQueuedItem);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Setup(s => s.AddTaskFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()))
.Callback(PrepareClientToReturnQueuedItem);
}
protected override RemoteMovie CreateRemoteMovie()
{
var movie = base.CreateRemoteMovie();
movie.Release.DownloadUrl = _downloadURL;
return movie;
}
[Test]
public void Download_with_DestinationDirectory_should_force_directory()
{
GivenDestinationDirectory();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), _encodedDestinationDirectory, It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[Test]
public void Download_with_Category_should_force_directory()
{
GivenDownloadConfiguration();
GivenCategory();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), _encodedDefaultDestinationAndCategory, It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[Test]
public void Download_without_DestinationDirectory_and_Category_should_use_default()
{
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), _encodedDefaultDestination, It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[TestCase(false, false)]
[TestCase(true, true)]
public void Download_should_pause_torrent_as_expected(bool addPausedSetting, bool toBePausedFlag)
{
_settings.AddPaused = addPausedSetting;
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), toBePausedFlag, It.IsAny<bool>(), It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[TestCase(0, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.First, true)]
[TestCase(0, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.First, true)]
[TestCase(0, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.Last, false)]
[TestCase(0, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.Last, false)]
[TestCase(22, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.First, true)]
[TestCase(22, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.First, false)]
[TestCase(22, (int)FreeboxDownloadPriority.First, (int)FreeboxDownloadPriority.Last, true)]
[TestCase(22, (int)FreeboxDownloadPriority.Last, (int)FreeboxDownloadPriority.Last, false)]
public void Download_should_queue_torrent_first_as_expected(int ageDay, int olderPriority, int recentPriority, bool toBeQueuedFirstFlag)
{
_settings.OlderPriority = olderPriority;
_settings.RecentPriority = recentPriority;
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
remoteMovie.Movie.MovieMetadata.Value.PhysicalRelease = DateTime.UtcNow.AddDays(-ageDay);
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), toBeQueuedFirstFlag, It.IsAny<double?>(), It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[TestCase(0, 0)]
[TestCase(1.5, 150)]
public void Download_should_define_seed_ratio_as_expected(double? providerSeedRatio, double? expectedSeedRatio)
{
GivenDownloadConfiguration();
GivenSuccessfulDownload();
var remoteMovie = CreateRemoteMovie();
remoteMovie.SeedConfiguration = new TorrentSeedConfiguration();
remoteMovie.SeedConfiguration.Ratio = providerSeedRatio;
Subject.Download(remoteMovie);
Mocker.GetMock<IFreeboxDownloadProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>(), expectedSeedRatio, It.IsAny<FreeboxDownloadSettings>()), Times.Once());
}
[Test]
public void GetItems_should_return_empty_list_if_no_tasks_available()
{
GivenTasks(new List<FreeboxDownloadTask>());
Subject.GetItems().Should().BeEmpty();
}
[Test]
public void GetItems_should_return_ignore_tasks_of_unknown_type()
{
_task.Status = FreeboxDownloadTaskStatus.Done;
_task.Type = "toto";
GivenTasks(new List<FreeboxDownloadTask> { _task });
Subject.GetItems().Should().BeEmpty();
}
[Test]
public void GetItems_when_destinationdirectory_is_set_should_ignore_downloads_in_wrong_folder()
{
_settings.DestinationDirectory = @"/some/path/that/will/not/match";
_task.Status = FreeboxDownloadTaskStatus.Done;
GivenTasks(new List<FreeboxDownloadTask> { _task });
Subject.GetItems().Should().BeEmpty();
}
[Test]
public void GetItems_when_category_is_set_should_ignore_downloads_in_wrong_folder()
{
_settings.Category = "somecategory";
_task.Status = FreeboxDownloadTaskStatus.Done;
GivenTasks(new List<FreeboxDownloadTask> { _task });
Subject.GetItems().Should().BeEmpty();
}
[TestCase(FreeboxDownloadTaskStatus.Downloading, false, false)]
[TestCase(FreeboxDownloadTaskStatus.Done, true, true)]
[TestCase(FreeboxDownloadTaskStatus.Seeding, false, false)]
[TestCase(FreeboxDownloadTaskStatus.Stopped, false, false)]
public void GetItems_should_return_canBeMoved_and_canBeDeleted_as_expected(FreeboxDownloadTaskStatus apiStatus, bool canMoveFilesExpected, bool canBeRemovedExpected)
{
_task.Status = apiStatus;
GivenTasks(new List<FreeboxDownloadTask>() { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().CanBeRemoved.Should().Be(canBeRemovedExpected);
items.First().CanMoveFiles.Should().Be(canMoveFilesExpected);
}
[TestCase(FreeboxDownloadTaskStatus.Stopped, DownloadItemStatus.Paused)]
[TestCase(FreeboxDownloadTaskStatus.Stopping, DownloadItemStatus.Paused)]
[TestCase(FreeboxDownloadTaskStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(FreeboxDownloadTaskStatus.Starting, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Retry, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Checking, DownloadItemStatus.Downloading)]
[TestCase(FreeboxDownloadTaskStatus.Error, DownloadItemStatus.Warning)]
[TestCase(FreeboxDownloadTaskStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(FreeboxDownloadTaskStatus.Done, DownloadItemStatus.Completed)]
[TestCase(FreeboxDownloadTaskStatus.Unknown, DownloadItemStatus.Downloading)]
public void GetItems_should_return_item_as_downloadItemStatus(FreeboxDownloadTaskStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_task.Status = apiStatus;
GivenTasks(new List<FreeboxDownloadTask>() { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().Status.Should().Be(expectedItemStatus);
}
[Test]
public void GetItems_should_return_decoded_destination_directory()
{
var decodedDownloadDirectory = "/that/the/path";
_task.Status = FreeboxDownloadTaskStatus.Done;
_task.DownloadDirectory = "L3RoYXQvdGhlL3BhdGg=";
GivenTasks(new List<FreeboxDownloadTask> { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().OutputPath.Should().Be(decodedDownloadDirectory);
}
[Test]
public void GetItems_should_return_message_if_tasks_in_error()
{
_task.Status = FreeboxDownloadTaskStatus.Error;
_task.Error = "internal";
GivenTasks(new List<FreeboxDownloadTask> { _task });
var items = Subject.GetItems();
items.Should().HaveCount(1);
items.First().Message.Should().Be("Internal error.");
items.First().Status.Should().Be(DownloadItemStatus.Warning);
}
}
}
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
@@ -276,7 +275,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
[TestCase(-1)] // Infinite/Unknown [TestCase(-1)] // Infinite/Unknown
[TestCase(-2)] // Magnet Downloading [TestCase(-2)] // Magnet Downloading
public void should_ignore_negative_eta(long eta) public void should_ignore_negative_eta(int eta)
{ {
_completed.Eta = eta; _completed.Eta = eta;
@@ -285,26 +284,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
item.RemainingTime.Should().NotHaveValue(); item.RemainingTime.Should().NotHaveValue();
} }
[TestCase(2147483648)] // 2038-01-19T03:14:08Z > int.MaxValue as unix timestamp can be either an int or a long
public void should_support_long_values_for_eta_in_seconds(long eta)
{
_downloading.Eta = eta;
PrepareClientToReturnDownloadingItem();
var item = Subject.GetItems().Single();
item.RemainingTime.Should().Be(TimeSpan.FromSeconds(eta));
}
[TestCase(2147483648000)] // works with milliseconds format too
public void should_support_long_values_for_eta_in_milliseconds(long eta)
{
_downloading.Eta = eta;
PrepareClientToReturnDownloadingItem();
var item = Subject.GetItems().Single();
item.RemainingTime.Should().Be(TimeSpan.FromMilliseconds(eta));
}
[Test] [Test]
public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_reached_and_not_stopped() public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_reached_and_not_stopped()
{ {
@@ -271,7 +271,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
[TestCase(-1)] // Infinite/Unknown [TestCase(-1)] // Infinite/Unknown
[TestCase(-2)] // Magnet Downloading [TestCase(-2)] // Magnet Downloading
public void should_ignore_negative_eta(long eta) public void should_ignore_negative_eta(int eta)
{ {
_completed.Eta = eta; _completed.Eta = eta;
@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.Download
_downloadClients = new List<IDownloadClient>(); _downloadClients = new List<IDownloadClient>();
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(v => v.GetDownloadClients(It.IsAny<bool>())) .Setup(v => v.GetDownloadClients())
.Returns(_downloadClients); .Returns(_downloadClients);
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
@@ -89,7 +89,6 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
.With(h => h.Title = title) .With(h => h.Title = title)
.With(h => h.Release = release) .With(h => h.Release = release)
.With(h => h.Reason = reason) .With(h => h.Reason = reason)
.With(h => h.ParsedMovieInfo = _parsedMovieInfo)
.Build(); .Build();
_heldReleases.AddRange(heldReleases); _heldReleases.AddRange(heldReleases);
@@ -52,9 +52,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
_pending.Add(new PendingRelease _pending.Add(new PendingRelease
{ {
Id = id, Id = id,
Title = "Movie.Title.2020.720p-Radarr",
ParsedMovieInfo = new ParsedMovieInfo { MovieTitles = new List<string> { title }, Year = year }, ParsedMovieInfo = new ParsedMovieInfo { MovieTitles = new List<string> { title }, Year = year },
Release = Builder<ReleaseInfo>.CreateNew().Build(),
MovieId = _movie.Id MovieId = _movie.Id
}); });
} }
@@ -93,7 +93,6 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
.With(h => h.MovieId = _movie.Id) .With(h => h.MovieId = _movie.Id)
.With(h => h.Title = title) .With(h => h.Title = title)
.With(h => h.Release = release) .With(h => h.Release = release)
.With(h => h.ParsedMovieInfo = _parsedMovieInfo)
.Build(); .Build();
Mocker.GetMock<IPendingReleaseRepository>() Mocker.GetMock<IPendingReleaseRepository>()
@@ -62,7 +62,7 @@ namespace NzbDrone.Core.Test.Extras.Others
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count.Should().Be(1); results.Count().Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true); results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
} }
@@ -78,7 +78,7 @@ namespace NzbDrone.Core.Test.Extras.Others
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count.Should().Be(1); results.Count().Should().Be(1);
} }
} }
} }
@@ -67,7 +67,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count.Should().Be(0); results.Count().Should().Be(0);
} }
[Test] [Test]
@@ -84,7 +84,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count.Should().Be(1); results.Count().Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true); results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
} }
@@ -110,7 +110,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count.Should().Be(expectedOutputs.Length); results.Count().Should().Be(expectedOutputs.Length);
for (int i = 0; i < expectedOutputs.Length; i++) for (int i = 0; i < expectedOutputs.Length; i++)
{ {
@@ -139,7 +139,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count.Should().Be(expectedOutputs.Length); results.Count().Should().Be(expectedOutputs.Length);
for (int i = 0; i < expectedOutputs.Length; i++) for (int i = 0; i < expectedOutputs.Length; i++)
{ {
@@ -169,7 +169,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
var results = Subject.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true).ToList();
results.Count.Should().Be(1); results.Count().Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true); results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
File diff suppressed because it is too large Load Diff
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
public void should_return_warning_when_download_client_has_not_been_configured() public void should_return_warning_when_download_client_has_not_been_configured()
{ {
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients())
.Returns(Array.Empty<IDownloadClient>()); .Returns(Array.Empty<IDownloadClient>());
Subject.Check().ShouldBeWarning(); Subject.Check().ShouldBeWarning();
@@ -40,7 +40,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Throws<Exception>(); .Throws<Exception>();
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { downloadClient.Object }); .Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeError(); Subject.Check().ShouldBeError();
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(new List<DownloadClientItem>()); .Returns(new List<DownloadClientItem>());
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { downloadClient.Object }); .Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeOk(); Subject.Check().ShouldBeOk();
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus); .Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { _downloadClient.Object }); .Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
@@ -1,80 +0,0 @@
using System;
using System.Collections.Generic;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Localization;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.HealthCheck.Checks
{
[TestFixture]
public class DownloadClientFolderCheckFixture : CoreTest<DownloadClientSortingCheck>
{
private DownloadClientInfo _clientStatus;
private Mock<IDownloadClient> _downloadClient;
private static Exception[] DownloadClientExceptions =
{
new DownloadClientUnavailableException("error"),
new DownloadClientAuthenticationException("error"),
new DownloadClientException("error")
};
[SetUp]
public void Setup()
{
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
_clientStatus = new DownloadClientInfo
{
IsLocalhost = true,
SortingMode = null
};
_downloadClient = Mocker.GetMock<IDownloadClient>();
_downloadClient.Setup(s => s.Definition)
.Returns(new DownloadClientDefinition { Name = "Test" });
_downloadClient.Setup(s => s.GetStatus())
.Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { _downloadClient.Object });
}
[Test]
public void should_return_ok_if_sorting_is_not_enabled()
{
Subject.Check().ShouldBeOk();
}
[Test]
public void should_return_warning_if_sorting_is_enabled()
{
_clientStatus.SortingMode = "TV";
Subject.Check().ShouldBeWarning();
}
[Test]
[TestCaseSource("DownloadClientExceptions")]
public void should_return_ok_if_client_throws_downloadclientexception(Exception ex)
{
_downloadClient.Setup(s => s.GetStatus())
.Throws(ex);
Subject.Check().ShouldBeOk();
ExceptionVerification.ExpectedErrors(0);
}
}
}
@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus); .Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { _downloadClient.Object }); .Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IConfigService>() Mocker.GetMock<IConfigService>()
@@ -1,133 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{
[TestFixture]
public class CleanupQualityProfileFormatItemsFixture : DbTest<CleanupQualityProfileFormatItems, Profile>
{
[SetUp]
public void Setup()
{
Mocker.SetConstant<IQualityProfileFormatItemsCleanupRepository>(
new QualityProfileFormatItemsCleanupRepository(Mocker.Resolve<IMainDatabase>(), Mocker.Resolve<IEventAggregator>()));
Mocker.SetConstant<ICustomFormatRepository>(
new CustomFormatRepository(Mocker.Resolve<IMainDatabase>(), Mocker.Resolve<IEventAggregator>()));
}
[Test]
public void should_remove_orphaned_custom_formats()
{
var qualityProfile = Builder<Profile>.CreateNew()
.With(h => h.Items = Qualities.QualityFixture.GetDefaultQualities())
.With(h => h.MinFormatScore = 50)
.With(h => h.CutoffFormatScore = 100)
.With(h => h.FormatItems = new List<ProfileFormatItem>
{
Builder<ProfileFormatItem>.CreateNew()
.With(c => c.Format = new CustomFormat("My Custom Format") { Id = 0 })
.Build()
})
.BuildNew();
Db.Insert(qualityProfile);
Subject.Clean();
var result = AllStoredModels;
result.Should().HaveCount(1);
result.First().FormatItems.Should().BeEmpty();
result.First().MinFormatScore.Should().Be(0);
result.First().CutoffFormatScore.Should().Be(0);
}
[Test]
public void should_not_remove_unorphaned_custom_formats()
{
var minFormatScore = 50;
var cutoffFormatScore = 100;
var customFormat = Builder<CustomFormat>.CreateNew()
.With(h => h.Specifications = new List<ICustomFormatSpecification>())
.BuildNew();
Db.Insert(customFormat);
var qualityProfile = Builder<Profile>.CreateNew()
.With(h => h.Items = Qualities.QualityFixture.GetDefaultQualities())
.With(h => h.MinFormatScore = minFormatScore)
.With(h => h.CutoffFormatScore = cutoffFormatScore)
.With(h => h.FormatItems = new List<ProfileFormatItem>
{
Builder<ProfileFormatItem>.CreateNew()
.With(c => c.Format = customFormat)
.Build()
})
.BuildNew();
Db.Insert(qualityProfile);
Subject.Clean();
var result = AllStoredModels;
result.Should().HaveCount(1);
result.First().FormatItems.Should().HaveCount(1);
result.First().MinFormatScore.Should().Be(minFormatScore);
result.First().CutoffFormatScore.Should().Be(cutoffFormatScore);
}
[Test]
public void should_add_missing_custom_formats()
{
var minFormatScore = 50;
var cutoffFormatScore = 100;
var customFormat1 = Builder<CustomFormat>.CreateNew()
.With(h => h.Id = 1)
.With(h => h.Name = "Custom Format 1")
.With(h => h.Specifications = new List<ICustomFormatSpecification>())
.BuildNew();
var customFormat2 = Builder<CustomFormat>.CreateNew()
.With(h => h.Id = 2)
.With(h => h.Name = "Custom Format 2")
.With(h => h.Specifications = new List<ICustomFormatSpecification>())
.BuildNew();
Db.Insert(customFormat1);
Db.Insert(customFormat2);
var qualityProfile = Builder<Profile>.CreateNew()
.With(h => h.Items = Qualities.QualityFixture.GetDefaultQualities())
.With(h => h.MinFormatScore = minFormatScore)
.With(h => h.CutoffFormatScore = cutoffFormatScore)
.With(h => h.FormatItems = new List<ProfileFormatItem>
{
Builder<ProfileFormatItem>.CreateNew()
.With(c => c.Format = customFormat1)
.Build()
})
.BuildNew();
Db.Insert(qualityProfile);
Subject.Clean();
var result = AllStoredModels;
result.Should().HaveCount(1);
result.First().FormatItems.Should().HaveCount(2);
result.First().MinFormatScore.Should().Be(minFormatScore);
result.First().CutoffFormatScore.Should().Be(cutoffFormatScore);
}
}
}
@@ -1,10 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.FileList;
using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Indexers.Omgwtfnzbs;
using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.IndexerTests
_indexers = new List<IIndexer>(); _indexers = new List<IIndexer>();
_indexers.Add(Mocker.Resolve<Newznab>()); _indexers.Add(Mocker.Resolve<Newznab>());
_indexers.Add(Mocker.Resolve<FileList>()); _indexers.Add(Mocker.Resolve<Omgwtfnzbs>());
Mocker.SetConstant<IEnumerable<IIndexer>>(_indexers); Mocker.SetConstant<IEnumerable<IIndexer>>(_indexers);
} }
@@ -62,21 +62,13 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
releaseInfo.Size.Should().Be(1183105773); releaseInfo.Size.Should().Be(1183105773);
} }
public void should_use_best_pagesize_reported_by_caps() [Test]
public void should_use_pagesize_reported_by_caps()
{ {
_caps.MaxPageSize = 30; _caps.MaxPageSize = 30;
_caps.DefaultPageSize = 25; _caps.DefaultPageSize = 25;
Subject.PageSize.Should().Be(30); Subject.PageSize.Should().Be(25);
}
[Test]
public void should_not_use_pagesize_over_100_even_if_reported_in_caps()
{
_caps.MaxPageSize = 250;
_caps.DefaultPageSize = 25;
Subject.PageSize.Should().Be(100);
} }
} }
} }
@@ -0,0 +1,56 @@
using System;
using System.Linq;
using System.Net.Http;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Omgwtfnzbs;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.IndexerTests.OmgwtfnzbsTests
{
[TestFixture]
public class OmgwtfnzbsFixture : CoreTest<Omgwtfnzbs>
{
[SetUp]
public void Setup()
{
Subject.Definition = new IndexerDefinition()
{
Name = "Omgwtfnzbs",
Settings = new OmgwtfnzbsSettings()
{
ApiKey = "xxx",
Username = "me@my.domain"
}
};
}
[Test]
public void should_parse_recent_feed_from_omgwtfnzbs()
{
var recentFeed = ReadAllText(@"Files/Indexers/Omgwtfnzbs/Omgwtfnzbs.xml");
Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
var releases = Subject.FetchRecent();
releases.Should().HaveCount(100);
var releaseInfo = releases.First();
releaseInfo.Title.Should().Be("Un.Petit.Boulot.2016.FRENCH.720p.BluRay.DTS.x264-LOST");
releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Usenet);
releaseInfo.DownloadUrl.Should().Be("https://api.omgwtfnzbs.me/nzb/?id=8a2Bw&user=nzbdrone&api=nzbdrone");
releaseInfo.InfoUrl.Should().Be("https://omgwtfnzbs.me/details.php?id=8a2Bw");
releaseInfo.CommentUrl.Should().BeNullOrEmpty();
releaseInfo.Indexer.Should().Be(Subject.Definition.Name);
releaseInfo.PublishDate.Should().Be(DateTime.Parse("2017/01/09 00:16:54"));
releaseInfo.Size.Should().Be(5354909355);
}
}
}
@@ -1,4 +1,3 @@
using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using FluentAssertions; using FluentAssertions;
@@ -56,7 +55,7 @@ namespace NzbDrone.Core.Test.IndexerTests.PTPTests
first.DownloadUrl.Should().Be("https://passthepopcorn.me/torrents.php?action=download&id=452135&authkey=00000000000000000000000000000000&torrent_pass=00000000000000000000000000000000"); first.DownloadUrl.Should().Be("https://passthepopcorn.me/torrents.php?action=download&id=452135&authkey=00000000000000000000000000000000&torrent_pass=00000000000000000000000000000000");
first.InfoUrl.Should().Be("https://passthepopcorn.me/torrents.php?id=148131&torrentid=452135"); first.InfoUrl.Should().Be("https://passthepopcorn.me/torrents.php?id=148131&torrentid=452135");
first.PublishDate.Should().Be(DateTime.Parse("2016-10-18T23:40:59+0000").ToUniversalTime()); // first.PublishDate.Should().Be(DateTime.Parse("2017-04-17T12:13:42+0000").ToUniversalTime()); stupid timezones
first.Size.Should().Be(2466170624L); first.Size.Should().Be(2466170624L);
first.InfoHash.Should().BeNullOrEmpty(); first.InfoHash.Should().BeNullOrEmpty();
first.MagnetUrl.Should().BeNullOrEmpty(); first.MagnetUrl.Should().BeNullOrEmpty();

Some files were not shown because too many files have changed in this diff Show More