1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-10 15:10:57 -04:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Qstick
72062704ff Aphrodite merged health check 2020-09-26 22:49:59 -04:00
188 changed files with 1466 additions and 3283 deletions

View File

@@ -185,9 +185,17 @@ dotnet_diagnostic.CA1814.severity = suggestion
dotnet_diagnostic.CA1815.severity = suggestion
dotnet_diagnostic.CA1816.severity = suggestion
dotnet_diagnostic.CA1819.severity = suggestion
dotnet_diagnostic.CA1820.severity = suggestion
dotnet_diagnostic.CA1821.severity = suggestion
dotnet_diagnostic.CA1822.severity = suggestion
dotnet_diagnostic.CA1823.severity = suggestion
dotnet_diagnostic.CA1824.severity = suggestion
dotnet_diagnostic.CA1825.severity = suggestion
dotnet_diagnostic.CA1826.severity = suggestion
dotnet_diagnostic.CA1827.severity = suggestion
dotnet_diagnostic.CA1828.severity = suggestion
dotnet_diagnostic.CA1829.severity = suggestion
dotnet_diagnostic.CA1834.severity = suggestion
dotnet_diagnostic.CA2000.severity = suggestion
dotnet_diagnostic.CA2002.severity = suggestion
dotnet_diagnostic.CA2007.severity = suggestion

View File

@@ -6,7 +6,7 @@
**Just because you receive an exception in your logs, doesn't mean it's a bug and should be reported here. Often it's something else, such as a permission error. If you are unsure ask on the Discord or Subreddit first.**
Visit our [Discord server](https://discord.gg/r5wJPt9) or [Subreddit](https://reddit.com/r/radarr) for support or longer discussions. Support questions posed on here will be closed immediately.
Visit our [Discord server](https://discord.gg/NWYch8M) or [Subreddit](https://reddit.com/r/radarr) for support or longer discussions. Support questions posed on here will be closed immediately.
Provide a description of the feature request or bug here, the more details the better.
Please also include the following if you are reporting a bug. If you do not include it, the issue will probably be closed as we cannot help you. -->

View File

@@ -1,7 +1,7 @@
blank_issues_enabled: false
contact_links:
- name: Support via Discord
url: https://discord.gg/r5wJPt9
url: https://discord.gg/AD3UP37
about: Chat with users and devs on support and setup related topics.
- name: Support via Reddit
url: https://reddit.com/r/radarr

5
.github/stale.yml vendored
View File

@@ -7,10 +7,7 @@ exemptLabels:
- feature request
- parser
- confirmed
- sonarr-pull
- lidarr-pull
- readarr-pull
- v3
- aphrodite
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable

2
.github/support.yml vendored
View File

@@ -6,7 +6,7 @@ supportLabel: support
# to a support page, or set to `false` to disable
supportComment: >
We use the issue tracker exclusively for bug reports and feature requests.
However, this issue appears to be a support request. Please hop over onto our [Discord](https://discord.gg/r5wJPt9) or [Subreddit](https://reddit.com/r/radarr)
However, this issue appears to be a support request. Please hop over onto our [Discord](https://discord.gg/ZDmT7qb) or [Subreddit](https://reddit.com/r/radarr)
# Whether to close issues marked as support requests
close: true
# Whether to lock issues marked as support requests

View File

@@ -14,7 +14,7 @@ See the readme for information on setting up your development environment.
- Rebase from Radarr's develop branch, don't merge
- Make meaningful commits, or squash them
- Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements
- Reach out to us on the discord if you have any questions
- Reach out to us on the forums or on IRC if you have any questions
- Add tests (unit/integration)
- Commit with *nix line endings for consistency (We checkout Windows and commit *nix)
- One feature/bug fix per pull request to keep things clean and easy to understand

View File

@@ -28,18 +28,15 @@ If you are using Docker please ensure your Docker paths are setup correctly usin
## Downloads
Please note that v0.2 will only have critical bugs resolved as of August 2020. Any additional development or features will be soley in V3.
Each push to the "develop" branch creates a build on "nightly" release channel (release channel is the "branch" within radarr's settings), once we push a build to Github it will show up on "develop" release channel.
| Release Channel Type | Branch: develop (stable) (v0.2) | Branch: nightly (semi-unstable) (v3.0) |
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Binary Releases | [![GitHub Releases](https://img.shields.io/badge/downloads-releases-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/releases) | [![Azure Build](https://img.shields.io/badge/downloads-Windows_X64-green.svg?maxAge=60&style=flat-square)](https://radarr.servarr.com/v1/update/nightly/updatefile?os=windows&runtime=netcore&arch=x64) <br> [![Azure Build](https://img.shields.io/badge/downloads-Linux_X64-green.svg?maxAge=60&style=flat-square)](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=x64) <br> [![Azure Build](https://img.shields.io/badge/downloads-Linux_ARM64-green.svg?maxAge=60&style=flat-square)](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=arm64) [![Azure Build](https://img.shields.io/badge/downloads-Linux_ARM-green.svg?maxAge=60&style=flat-square)](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=arm) <br> [![Azure Build](https://img.shields.io/badge/downloads-macOS-green.svg?maxAge=60&style=flat-square)](https://radarr.servarr.com/v1/update/nightly/updatefile?os=osx&runtime=netcore&arch=x64)
| Docker - lsio | [![Docker release](https://img.shields.io/badge/linuxserver-radarr:latest-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/linuxserver/radarr) | [![Docker nightly](https://img.shields.io/badge/linuxserver-radarr:nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/linuxserver/radarr) |
| Docker - hotio | [![Docker release](https://img.shields.io/badge/hotio-radarr:latest-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) | [![Docker nightly](https://img.shields.io/badge/hotio-radarr:nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) |
| Release Type | Branch: develop (stable) | Branch: nightly (semi-unstable) | Branch: aphrodite (very-unstable) |
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Binary Releases | [![GitHub Releases](https://img.shields.io/badge/downloads-releases-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/releases) | [![AppVeyor Builds](https://img.shields.io/badge/downloads-nightly-green.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/radarr-usby1/branch/develop/artifacts) | |
| Docker - lsio | [![Docker release](https://img.shields.io/badge/linuxserver-radarr:latest-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/linuxserver/radarr) | [![Docker nightly](https://img.shields.io/badge/linuxserver-radarr:nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/linuxserver/radarr) | [![Docker aphrodite](https://img.shields.io/badge/linuxserver-radarr:preview-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/linuxserver/radarr) |
| Docker - hotio | [![Docker release](https://img.shields.io/badge/hotio-radarr:latest-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) | [![Docker nightly](https://img.shields.io/badge/hotio-radarr:unstable-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) | [![Docker aphrodite](https://img.shields.io/badge/hotio-radarr:aphrodite-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) |
## Support
[![Discord](https://img.shields.io/badge/discord-chat-r5wJPt9.svg?maxAge=60&style=flat-square)](https://discord.gg/r5wJPt9)
[![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60&style=flat-square)](https://discord.gg/AD3UP37)
[![Reddit](https://img.shields.io/badge/reddit-discussion-FF4500.svg?maxAge=60&style=flat-square)](https://www.reddit.com/r/radarr)
[![GitHub](https://img.shields.io/badge/github-issues-red.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/issues)
[![GitHub Wiki](https://img.shields.io/badge/github-wiki-181717.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/wiki)

View File

@@ -20,10 +20,11 @@ trigger:
branches:
include:
- develop
- master
- aphrodite
pr:
- develop
- aphrodite
stages:
- stage: Setup
@@ -39,7 +40,7 @@ stages:
displayName: Set Build Name
- bash: |
if [[ $BUILD_REASON == "PullRequest" ]]; then
git diff origin/develop...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
git diff origin/aphrodite...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
echo $? > not_backend_update
else
echo 0 > not_backend_update
@@ -301,22 +302,14 @@ stages:
sentry-cli releases new --finalize -p radarr -p radarr-ui -p radarr-update "${RELEASENAME}"
sentry-cli releases -p radarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
sentry-cli releases set-commits --auto "${RELEASENAME}"
if [[ ${BUILD_SOURCEBRANCH} == "refs/heads/develop" ]]; then
sentry-cli releases deploys "${RELEASENAME}" new -e nightly
else
sentry-cli releases deploys "${RELEASENAME}" new -e production
fi
sentry-cli releases deploys "${RELEASENAME}" new -e aphrodite
if [ $? -gt 0 ]; then
echo "##vso[task.logissue type=warning]Error uploading source maps."
fi
exit 0
displayName: Publish Sentry Source Maps
condition: |
or
(
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
)
continueOnError: true
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/aphrodite'))
env:
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
SENTRY_ORG: $(sentryOrg)

View File

@@ -75,7 +75,7 @@
"function-parentheses-newline-inside": "never-multi-line",
"function-parentheses-space-inside": "never",
"function-url-quotes": "always",
"function-url-scheme-disallowed-list": [
"function-url-scheme-blacklist": [
"data"
],
"function-whitespace-after": "always",

View File

@@ -7,7 +7,6 @@ const errorHandler = require('./helpers/errorHandler');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackPluginHtmlTags = require('html-webpack-plugin/lib/html-tags');
const TerserPlugin = require('terser-webpack-plugin');
const uiFolder = 'UI';
@@ -15,7 +14,7 @@ const frontendFolder = path.join(__dirname, '..');
const srcFolder = path.join(frontendFolder, 'src');
const isProduction = process.argv.indexOf('--production') > -1;
const isProfiling = isProduction && process.argv.indexOf('--profile') > -1;
const inlineWebWorkers = 'no-fallback';
const inlineWebWorkers = true;
const distFolder = path.resolve(frontendFolder, '..', '_output', uiFolder);
@@ -33,19 +32,14 @@ const cssVarsFiles = [
].map(require.resolve);
// Override the way HtmlWebpackPlugin injects the scripts
// TODO: Find a better way to get these paths without
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, assets, assetTags) {
const head = assetTags.headTags.map((v) => {
const href = v.attributes.href
.replace('\\', '/')
.replace('%5C', '/');
v.attributes = { rel: 'stylesheet', type: 'text/css', href: `/${href}` };
return HtmlWebpackPluginHtmlTags.htmlTagObjectToString(v, this.options.xhtml);
const head = assetTags.head.map((v) => {
v.attributes = { rel: 'stylesheet', type: 'text/css', href: `/${v.attributes.href.replace('\\', '/')}` };
return this.createHtmlTag(v);
});
const body = assetTags.bodyTags.map((v) => {
const body = assetTags.body.map((v) => {
v.attributes = { src: `/${v.attributes.src}` };
return HtmlWebpackPluginHtmlTags.htmlTagObjectToString(v, this.options.xhtml);
return this.createHtmlTag(v);
});
return html
@@ -131,8 +125,9 @@ const config = {
use: {
loader: 'worker-loader',
options: {
filename: '[name].js',
inline: inlineWebWorkers
name: '[name].js',
inline: inlineWebWorkers,
fallback: !inlineWebWorkers
}
}
},

View File

@@ -1,3 +1,4 @@
import _ from 'lodash';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
@@ -7,10 +8,10 @@ function createMapStateToProps() {
return createSelector(
createUISettingsSelector(),
(uiSettings) => {
return {
shortDateFormat: uiSettings.shortDateFormat,
timeFormat: uiSettings.timeFormat
};
return _.pick(uiSettings, [
'shortDateFormat',
'timeFormat'
]);
}
);
}

View File

@@ -11,14 +11,14 @@ function QueueDetails(props) {
size,
sizeleft,
estimatedCompletionTime,
status,
trackedDownloadState,
trackedDownloadStatus,
status: queueStatus,
errorMessage,
progressBar
} = props;
const progress = size ? (100 - sizeleft / size * 100) : 0;
const status = queueStatus.toLowerCase();
const progress = (100 - sizeleft / size * 100);
if (status === 'pending') {
return (
@@ -40,35 +40,7 @@ function QueueDetails(props) {
);
}
if (trackedDownloadStatus === 'warning') {
return (
<Icon
name={icons.DOWNLOAD}
kind={kinds.WARNING}
title={'Downloaded - Unable to Import: check logs for details'}
/>
);
}
if (trackedDownloadState === 'importPending') {
return (
<Icon
name={icons.DOWNLOAD}
kind={kinds.PURPLE}
title={`${translate('Downloaded')} - ${translate('WaitingToImport')}`}
/>
);
}
if (trackedDownloadState === 'importing') {
return (
<Icon
name={icons.DOWNLOAD}
kind={kinds.PURPLE}
title={`${translate('Downloaded')} - ${translate('Importing')}`}
/>
);
}
// TODO: show an icon when download is complete, but not imported yet?
}
if (errorMessage) {
@@ -119,8 +91,6 @@ QueueDetails.propTypes = {
sizeleft: PropTypes.number.isRequired,
estimatedCompletionTime: PropTypes.string,
status: PropTypes.string.isRequired,
trackedDownloadState: PropTypes.string.isRequired,
trackedDownloadStatus: PropTypes.string.isRequired,
errorMessage: PropTypes.string,
progressBar: PropTypes.node.isRequired
};

View File

@@ -117,7 +117,7 @@ class AddNewMovieModalContent extends Component {
<FormGroup>
<FormLabel>
{translate('Monitor')}
Monitor
</FormLabel>
<FormInputGroup
@@ -168,7 +168,7 @@ class AddNewMovieModalContent extends Component {
<ModalFooter className={styles.modalFooter}>
<label className={styles.searchForMissingMovieLabelContainer}>
<span className={styles.searchForMissingMovieLabel}>
{translate('StartSearchForMissingMovie')}
Start search for missing movie
</span>
<CheckInput
@@ -186,7 +186,7 @@ class AddNewMovieModalContent extends Component {
isSpinning={isAdding}
onPress={this.onAddMoviePress}
>
{translate('AddMovie')}
Add {title}
</SpinnerButton>
</ModalFooter>
</ModalContent>

View File

@@ -170,7 +170,6 @@ class AddNewMovieSearchResult extends Component {
imdbId={imdbId}
/>
}
canFlip={true}
kind={kinds.INVERSE}
position={tooltipPositions.BOTTOM}
/>

View File

@@ -10,7 +10,6 @@ import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContentFooter from 'Components/Page/PageContentFooter';
import Popover from 'Components/Tooltip/Popover';
import { icons, inputTypes, kinds, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './ImportMovieFooter.css';
const MIXED = 'mixed';
@@ -113,7 +112,7 @@ class ImportMovieFooter extends Component {
<PageContentFooter>
<div className={styles.inputContainer}>
<div className={styles.label}>
{translate('Monitor')}
Monitor
</div>
<FormInputGroup
@@ -128,7 +127,7 @@ class ImportMovieFooter extends Component {
<div className={styles.inputContainer}>
<div className={styles.label}>
{translate('MinimumAvailability')}
Minimum Availability
</div>
<FormInputGroup
@@ -143,7 +142,7 @@ class ImportMovieFooter extends Component {
<div className={styles.inputContainer}>
<div className={styles.label}>
{translate('QualityProfile')}
Quality Profile
</div>
<FormInputGroup
@@ -169,7 +168,7 @@ class ImportMovieFooter extends Component {
isDisabled={!selectedCount || isLookingUpMovie}
onPress={onImportPress}
>
{translate('Import')} {selectedCount} {selectedCount > 1 ? translate('Movies') : translate('Movie')}
Import {selectedCount} {selectedCount > 1 ? 'Movies' : 'Movie'}
</SpinnerButton>
{
@@ -179,7 +178,7 @@ class ImportMovieFooter extends Component {
kind={kinds.WARNING}
onPress={onCancelLookupPress}
>
{translate('CancelProcessing')}
Cancel Processing
</Button> :
null
}
@@ -191,7 +190,7 @@ class ImportMovieFooter extends Component {
kind={kinds.SUCCESS}
onPress={onLookupPress}
>
{translate('StartProcessing')}
Start Processing
</Button> :
null
}
@@ -207,7 +206,7 @@ class ImportMovieFooter extends Component {
{
isLookingUpMovie ?
translate('ProcessingFolders') :
'Processing Folders' :
null
}
@@ -221,7 +220,7 @@ class ImportMovieFooter extends Component {
kind={kinds.WARNING}
/>
}
title={translate('ImportErrors')}
title="Import Errors"
body={
<ul>
{

View File

@@ -3,7 +3,6 @@ import React from 'react';
import VirtualTableHeader from 'Components/Table/VirtualTableHeader';
import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell';
import VirtualTableSelectAllHeaderCell from 'Components/Table/VirtualTableSelectAllHeaderCell';
import translate from 'Utilities/String/translate';
import styles from './ImportMovieHeader.css';
function ImportMovieHeader(props) {
@@ -25,35 +24,35 @@ function ImportMovieHeader(props) {
className={styles.folder}
name="folder"
>
{translate('Folder')}
Folder
</VirtualTableHeaderCell>
<VirtualTableHeaderCell
className={styles.monitor}
name="monitor"
>
{translate('Monitor')}
Monitor
</VirtualTableHeaderCell>
<VirtualTableHeaderCell
className={styles.minimumAvailability}
name="minimumAvailability"
>
{translate('MinAvailability')}
Min Availability
</VirtualTableHeaderCell>
<VirtualTableHeaderCell
className={styles.qualityProfile}
name="qualityProfileId"
>
{translate('QualityProfile')}
Quality Profile
</VirtualTableHeaderCell>
<VirtualTableHeaderCell
className={styles.movie}
name="movie"
>
{translate('Movie')}
Movie
</VirtualTableHeaderCell>
</VirtualTableHeader>
);

View File

@@ -9,7 +9,6 @@ import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import Portal from 'Components/Portal';
import { icons, kinds } from 'Helpers/Props';
import getUniqueElememtId from 'Utilities/getUniqueElementId';
import translate from 'Utilities/String/translate';
import ImportMovieSearchResultConnector from './ImportMovieSearchResultConnector';
import ImportMovieTitle from './ImportMovieTitle';
import styles from './ImportMovieSelectMovie.css';
@@ -175,7 +174,7 @@ class ImportMovieSelectMovie extends Component {
kind={kinds.WARNING}
/>
{translate('NoMatchFound')}
No match found!
</div> :
null
}
@@ -190,7 +189,7 @@ class ImportMovieSelectMovie extends Component {
kind={kinds.WARNING}
/>
{translate('SearchFailedPleaseTryAgainLater')}
Search failed, please try again later.
</div> :
null
}

View File

@@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
import React from 'react';
import Label from 'Components/Label';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './ImportMovieTitle.css';
function ImportMovieTitle(props) {
@@ -34,7 +33,7 @@ function ImportMovieTitle(props) {
<Label
kind={kinds.WARNING}
>
{translate('Existing')}
Existing
</Label>
}
</div>

View File

@@ -101,8 +101,12 @@ class ImportMovieSelectFolder extends Component {
<div className={styles.tips}>
{translate('ImportTipsMessage')}
<ul>
<li className={styles.tip} dangerouslySetInnerHTML={{ __html: translate('ImportIncludeQuality', ['<code>movie.2008.bluray.mkv</code>']) }} />
<li className={styles.tip} dangerouslySetInnerHTML={{ __html: translate('ImportRootPath', [`<code>${isWindows ? 'C:\\movies' : '/movies'}</code>`, `<code>${isWindows ? 'C:\\movies\\the matrix' : '/movies/the matrix'}</code>`]) }} />
<li className={styles.tip}>
Make sure that your files include the quality in their filenames. eg. <span className={styles.code}>movie.2008.bluray.mkv</span>
</li>
<li className={styles.tip}>
Point Radarr to the folder containing all of your movies, not a specific one. eg. <span className={styles.code}>"{isWindows ? 'C:\\movies' : '/movies'}"</span> and not <span className={styles.code}>"{isWindows ? 'C:\\movies\\the matrix' : '/movies/the matrix'}"</span>
</li>
</ul>
</div>
@@ -154,7 +158,7 @@ class ImportMovieSelectFolder extends Component {
className={styles.importButtonIcon}
name={icons.DRIVE}
/>
{translate('StartImport')}
Start Import
</Button>
</div>
}

View File

@@ -6,38 +6,9 @@ import styles from './Agenda.css';
function Agenda(props) {
const {
items,
start,
end
items
} = props;
const startDateParsed = Date.parse(start);
const endDateParsed = Date.parse(end);
items.forEach((item) => {
const cinemaDateParsed = Date.parse(item.inCinemas);
const digitalDateParsed = Date.parse(item.digitalRelease);
const physicalDateParsed = Date.parse(item.physicalRelease);
const dates = [];
if (cinemaDateParsed > 0 && cinemaDateParsed >= startDateParsed && cinemaDateParsed <= endDateParsed) {
dates.push(cinemaDateParsed);
}
if (digitalDateParsed > 0 && digitalDateParsed >= startDateParsed && digitalDateParsed <= endDateParsed) {
dates.push(digitalDateParsed);
}
if (physicalDateParsed > 0 && physicalDateParsed >= startDateParsed && physicalDateParsed <= endDateParsed) {
dates.push(physicalDateParsed);
}
item.sortDate = Math.min(...dates);
item.cinemaDateParsed = cinemaDateParsed;
item.digitalDateParsed = digitalDateParsed;
item.physicalDateParsed = physicalDateParsed;
});
items.sort((a, b) => ((a.sortDate > b.sortDate) ? 1 : -1));
return (
<div className={styles.agenda}>
{
@@ -61,9 +32,7 @@ function Agenda(props) {
}
Agenda.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired,
start: PropTypes.string.isRequired,
end: PropTypes.string.isRequired
items: PropTypes.arrayOf(PropTypes.object).isRequired
};
export default Agenda;

View File

@@ -92,5 +92,6 @@
}
.dateIcon {
width: 25px;
display: inline;
margin-right: 10px;
}

View File

@@ -55,26 +55,23 @@ class AgendaEvent extends Component {
showCutoffUnmetIcon,
longDateFormat,
colorImpairedMode,
cinemaDateParsed,
digitalDateParsed,
physicalDateParsed,
sortDate
startDate,
endDate
} = this.props;
let startTime = null;
let releaseIcon = null;
const agendaStart = Date.parse(startDate);
const agendaEnd = Date.parse(endDate);
const cinemaDate = Date.parse(inCinemas);
const digitalDate = Date.parse(digitalRelease);
let startTime = physicalRelease;
let releaseIcon = icons.DISC;
if (physicalDateParsed === sortDate) {
startTime = physicalRelease;
releaseIcon = icons.DISC;
}
if (digitalDateParsed === sortDate) {
if (digitalDate >= agendaStart && digitalDate <= agendaEnd) {
startTime = digitalRelease;
releaseIcon = icons.MOVIE_FILE;
}
if (cinemaDateParsed === sortDate) {
if (cinemaDate >= agendaStart && cinemaDate <= agendaEnd) {
startTime = inCinemas;
releaseIcon = icons.IN_CINEMAS;
}
@@ -95,14 +92,13 @@ class AgendaEvent extends Component {
)}
to={link}
>
<div className={styles.dateIcon}>
<Icon
name={releaseIcon}
kind={kinds.DEFAULT}
/>
</div>
<div className={styles.date}>
<div className={styles.dateIcon}>
<Icon
name={releaseIcon}
kind={kinds.DEFAULT}
/>
</div>
{(showDate) ? startTime.format(longDateFormat) : null}
</div>
@@ -180,10 +176,8 @@ AgendaEvent.propTypes = {
timeFormat: PropTypes.string.isRequired,
longDateFormat: PropTypes.string.isRequired,
colorImpairedMode: PropTypes.bool.isRequired,
cinemaDateParsed: PropTypes.number,
digitalDateParsed: PropTypes.number,
physicalDateParsed: PropTypes.number,
sortDate: PropTypes.number
startDate: PropTypes.date,
endDate: PropTypes.date
};
AgendaEvent.defaultProps = {

View File

@@ -13,7 +13,9 @@ function createMapStateToProps() {
createMovieFileSelector(),
createQueueItemSelector(),
createUISettingsSelector(),
(calendarOptions, movie, movieFile, queueItem, uiSettings) => {
(state) => state.calendar.start,
(state) => state.calendar.end,
(calendarOptions, movie, movieFile, queueItem, uiSettings, startDate, endDate) => {
return {
movie,
movieFile,
@@ -21,7 +23,9 @@ function createMapStateToProps() {
...calendarOptions,
timeFormat: uiSettings.timeFormat,
longDateFormat: uiSettings.longDateFormat,
colorImpairedMode: uiSettings.enableColorImpairedMode
colorImpairedMode: uiSettings.enableColorImpairedMode,
startDate,
endDate
};
}
);

View File

@@ -12,12 +12,10 @@ function CalendarEventQueueDetails(props) {
sizeleft,
estimatedCompletionTime,
status,
trackedDownloadState,
trackedDownloadStatus,
errorMessage
} = props;
const progress = size ? (100 - sizeleft / size * 100) : 0;
const progress = (100 - sizeleft / size * 100);
return (
<QueueDetails
@@ -26,8 +24,6 @@ function CalendarEventQueueDetails(props) {
sizeleft={sizeleft}
estimatedCompletionTime={estimatedCompletionTime}
status={status}
trackedDownloadState={trackedDownloadState}
trackedDownloadStatus={trackedDownloadStatus}
errorMessage={errorMessage}
progressBar={
<div title={translate('MovieIsDownloadingInterp', [progress.toFixed(1), title])}>
@@ -49,8 +45,6 @@ CalendarEventQueueDetails.propTypes = {
sizeleft: PropTypes.number.isRequired,
estimatedCompletionTime: PropTypes.string,
status: PropTypes.string.isRequired,
trackedDownloadState: PropTypes.string.isRequired,
trackedDownloadStatus: PropTypes.string.isRequired,
errorMessage: PropTypes.string
};

View File

@@ -24,7 +24,7 @@ function getTitle(time, start, end, view, longDateFormat) {
} else if (view === 'month') {
return timeMoment.format('MMMM YYYY');
} else if (view === 'agenda') {
return `Agenda: ${startMoment.format('MMM D')} - ${endMoment.format('MMM D')}`;
return 'Agenda';
}
let startFormat = 'MMM D YYYY';

View File

@@ -1,3 +1,4 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@@ -13,16 +14,19 @@ function createMapStateToProps() {
createDimensionsSelector(),
createUISettingsSelector(),
(calendar, dimensions, uiSettings) => {
return {
isFetching: calendar.isFetching,
view: calendar.view,
time: calendar.time,
start: calendar.start,
end: calendar.end,
isSmallScreen: dimensions.isSmallScreen,
collapseViewButtons: dimensions.isLargeScreen,
longDateFormat: uiSettings.longDateFormat
};
const result = _.pick(calendar, [
'isFetching',
'view',
'time',
'start',
'end'
]);
result.isSmallScreen = dimensions.isSmallScreen;
result.collapseViewButtons = dimensions.isLargeScreen;
result.longDateFormat = uiSettings.longDateFormat;
return result;
}
);
}

View File

@@ -58,30 +58,11 @@ function getSelectedIndex(props) {
values
} = props;
if (Array.isArray(value)) {
return values.findIndex((v) => {
return value.size && v.key === value[0];
});
}
return values.findIndex((v) => {
return v.key === value;
});
}
function isSelectedItem(index, props) {
const {
value,
values
} = props;
if (Array.isArray(value)) {
return value.includes(values[index].key);
}
return values[index].key === value;
}
function getKey(selectedIndex, values) {
return values[selectedIndex].key;
}
@@ -111,7 +92,7 @@ class EnhancedSelectInput extends Component {
this._scheduleUpdate();
}
if (!Array.isArray(this.props.value) && prevProps.value !== this.props.value) {
if (prevProps.value !== this.props.value) {
this.setState({
selectedIndex: getSelectedIndex(this.props)
});
@@ -153,7 +134,7 @@ class EnhancedSelectInput extends Component {
const button = document.getElementById(this._buttonId);
const options = document.getElementById(this._optionsId);
if (!button || !event.target.isConnected || this.state.isMobile) {
if (!button || this.state.isMobile) {
return;
}
@@ -196,7 +177,7 @@ class EnhancedSelectInput extends Component {
}
if (
selectedIndex == null || selectedIndex === -1 ||
selectedIndex == null ||
getSelectedOption(selectedIndex, values).isDisabled
) {
if (keyCode === keyCodes.UP_ARROW) {
@@ -254,27 +235,12 @@ class EnhancedSelectInput extends Component {
}
onSelect = (value) => {
if (Array.isArray(this.props.value)) {
let newValue = null;
const index = this.props.value.indexOf(value);
if (index === -1) {
newValue = this.props.values.map((v) => v.key).filter((v) => (v === value) || this.props.value.includes(v));
} else {
newValue = [...this.props.value];
newValue.splice(index, 1);
}
this.props.onChange({
name: this.props.name,
value: newValue
});
} else {
this.setState({ isOpen: false });
this.setState({ isOpen: false });
this.props.onChange({
name: this.props.name,
value
});
}
this.props.onChange({
name: this.props.name,
value
});
}
onMeasure = ({ width }) => {
@@ -292,7 +258,6 @@ class EnhancedSelectInput extends Component {
const {
className,
disabledClassName,
value,
values,
isDisabled,
hasError,
@@ -310,7 +275,6 @@ class EnhancedSelectInput extends Component {
isMobile
} = this.state;
const isMultiSelect = Array.isArray(value);
const selectedOption = getSelectedOption(selectedIndex, values);
return (
@@ -339,12 +303,9 @@ class EnhancedSelectInput extends Component {
onPress={this.onPress}
>
<SelectedValueComponent
value={value}
values={values}
{...selectedValueOptions}
{...selectedOption}
isDisabled={isDisabled}
isMultiSelect={isMultiSelect}
>
{selectedOption ? selectedOption.value : null}
</SelectedValueComponent>
@@ -398,17 +359,11 @@ class EnhancedSelectInput extends Component {
>
{
values.map((v, index) => {
const hasParent = v.parentKey !== undefined;
const depth = hasParent ? 1 : 0;
const parentSelected = hasParent && value.includes(v.parentKey);
return (
<OptionComponent
key={v.key}
id={v.key}
depth={depth}
isSelected={isSelectedItem(index, this.props)}
isDisabled={parentSelected}
isMultiSelect={isMultiSelect}
isSelected={index === selectedIndex}
{...valueOptions}
{...v}
isMobile={false}
@@ -446,17 +401,11 @@ class EnhancedSelectInput extends Component {
<Scroller className={styles.optionsModalScroller}>
{
values.map((v, index) => {
const hasParent = v.parentKey !== undefined;
const depth = hasParent ? 1 : 0;
const parentSelected = hasParent && value.includes(v.parentKey);
return (
<OptionComponent
key={v.key}
id={v.key}
depth={depth}
isSelected={isSelectedItem(index, this.props)}
isMultiSelect={isMultiSelect}
isDisabled={parentSelected}
isSelected={index === selectedIndex}
{...valueOptions}
{...v}
isMobile={true}
@@ -480,9 +429,9 @@ EnhancedSelectInput.propTypes = {
className: PropTypes.string,
disabledClassName: PropTypes.string,
name: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.arrayOf(PropTypes.number)]).isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
isDisabled: PropTypes.bool.isRequired,
isDisabled: PropTypes.bool,
hasError: PropTypes.bool,
hasWarning: PropTypes.bool,
valueOptions: PropTypes.object.isRequired,

View File

@@ -11,18 +11,6 @@
}
}
.optionCheck {
composes: container from '~./CheckInput.css';
flex: 0 0 0;
}
.optionCheckInput {
composes: input from '~./CheckInput.css';
margin-top: 0;
}
.isSelected {
background-color: #e2e2e2;

View File

@@ -4,7 +4,6 @@ import React, { Component } from 'react';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import { icons } from 'Helpers/Props';
import CheckInput from './CheckInput';
import styles from './EnhancedSelectInputOption.css';
class EnhancedSelectInputOption extends Component {
@@ -21,21 +20,15 @@ class EnhancedSelectInputOption extends Component {
onSelect(id);
}
onCheckPress = () => {
// CheckInput requires a handler. Swallow the change event because onPress will already handle it via event propagation.
}
//
// Render
render() {
const {
className,
id,
isSelected,
isDisabled,
isHidden,
isMultiSelect,
isMobile,
children
} = this.props;
@@ -44,8 +37,8 @@ class EnhancedSelectInputOption extends Component {
<Link
className={classNames(
className,
isSelected && !isMultiSelect && styles.isSelected,
isDisabled && !isMultiSelect && styles.isDisabled,
isSelected && styles.isSelected,
isDisabled && styles.isDisabled,
isHidden && styles.isHidden,
isMobile && styles.isMobile
)}
@@ -53,19 +46,6 @@ class EnhancedSelectInputOption extends Component {
isDisabled={isDisabled}
onPress={this.onPress}
>
{
isMultiSelect &&
<CheckInput
className={styles.optionCheckInput}
containerClassName={styles.optionCheck}
name={`select-${id}`}
value={isSelected}
isDisabled={isDisabled}
onChange={this.onCheckPress}
/>
}
{children}
{
@@ -87,7 +67,6 @@ EnhancedSelectInputOption.propTypes = {
isSelected: PropTypes.bool.isRequired,
isDisabled: PropTypes.bool.isRequired,
isHidden: PropTypes.bool.isRequired,
isMultiSelect: PropTypes.bool.isRequired,
isMobile: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
onSelect: PropTypes.func.isRequired
@@ -96,8 +75,7 @@ EnhancedSelectInputOption.propTypes = {
EnhancedSelectInputOption.defaultProps = {
className: styles.option,
isDisabled: false,
isHidden: false,
isMultiSelect: false
isHidden: false
};
export default EnhancedSelectInputOption;

View File

@@ -6,23 +6,14 @@ import styles from './HintedSelectInputOption.css';
function HintedSelectInputOption(props) {
const {
id,
value,
hint,
isSelected,
isDisabled,
isMultiSelect,
isMobile,
...otherProps
} = props;
return (
<EnhancedSelectInputOption
id={id}
isSelected={isSelected}
isDisabled={isDisabled}
isHidden={isDisabled}
isMultiSelect={isMultiSelect}
isMobile={isMobile}
{...otherProps}
>
@@ -45,19 +36,9 @@ function HintedSelectInputOption(props) {
}
HintedSelectInputOption.propTypes = {
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
value: PropTypes.string.isRequired,
hint: PropTypes.node,
isSelected: PropTypes.bool.isRequired,
isDisabled: PropTypes.bool.isRequired,
isMultiSelect: PropTypes.bool.isRequired,
isMobile: PropTypes.bool.isRequired
};
HintedSelectInputOption.defaultProps = {
isDisabled: false,
isHidden: false,
isMultiSelect: false
};
export default HintedSelectInputOption;

View File

@@ -1,43 +1,23 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import Label from 'Components/Label';
import EnhancedSelectInputSelectedValue from './EnhancedSelectInputSelectedValue';
import styles from './HintedSelectInputSelectedValue.css';
function HintedSelectInputSelectedValue(props) {
const {
value,
values,
hint,
isMultiSelect,
includeHint,
...otherProps
} = props;
const valuesMap = isMultiSelect && _.keyBy(values, 'key');
return (
<EnhancedSelectInputSelectedValue
className={styles.selectedValue}
{...otherProps}
>
<div className={styles.valueText}>
{
isMultiSelect &&
value.map((key, index) => {
const v = valuesMap[key];
return (
<Label key={key}>
{v ? v.value : key}
</Label>
);
})
}
{
!isMultiSelect && value
}
{value}
</div>
{
@@ -51,15 +31,12 @@ function HintedSelectInputSelectedValue(props) {
}
HintedSelectInputSelectedValue.propTypes = {
value: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))]).isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
value: PropTypes.string,
hint: PropTypes.string,
isMultiSelect: PropTypes.bool.isRequired,
includeHint: PropTypes.bool.isRequired
};
HintedSelectInputSelectedValue.defaultProps = {
isMultiSelect: false,
includeHint: true
};

View File

@@ -6,7 +6,7 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes } from 'Helpers/Props';
function getType(type, value) {
function getType(type) {
switch (type) {
case 'captcha':
return inputTypes.CAPTCHA;
@@ -45,8 +45,7 @@ function getSelectValues(selectOptions) {
return _.reduce(selectOptions, (result, option) => {
result.push({
key: option.value,
value: option.name,
hint: option.hint
value: option.name
});
return result;
@@ -88,7 +87,7 @@ function ProviderFieldFormGroup(props) {
<FormLabel>{label}</FormLabel>
<FormInputGroup
type={getType(type, value)}
type={getType(type)}
name={name}
label={label}
helpText={helpText}

View File

@@ -15,12 +15,10 @@
.value {
display: flex;
max-width: 500px;
}
.movieFolder {
@add-mixin truncate;
flex: 0 0 auto;
color: $disabledColor;
}

View File

@@ -2,24 +2,8 @@ import React from 'react';
import styles from './LoadingMessage.css';
const messages = [
'Downloading more RAM',
'Now in Technicolor',
'Previously on Radarr...',
'Bleep Bloop.',
'Locating the required gigapixels to render...',
'Spinning up the hamster wheel...',
'At least you\'re not on hold',
'Hum something loud while others stare',
'Loading humorous message... Please Wait',
'I could\'ve been faster in Python',
'Don\'t forget to rewind your tracks',
'Congratulations! you are the 1000th visitor.',
'HELP! I\'m being held hostage and forced to write these stupid lines!',
'RE-calibrating the internet...',
'I\'ll be here all week',
'Don\'t forget to tip your waitress',
'Apply directly to the forehead',
'Loading Battlestation'
'Welcome to Radarr Aphrodite Preview. Enjoy'
// TODO Add some messages here
];
let message = null;

View File

@@ -1,3 +1,4 @@
import _ from 'lodash';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
@@ -7,12 +8,12 @@ function createMapStateToProps() {
return createSelector(
createUISettingsSelector(),
(uiSettings) => {
return {
showRelativeDates: uiSettings.showRelativeDates,
shortDateFormat: uiSettings.shortDateFormat,
longDateFormat: uiSettings.longDateFormat,
timeFormat: uiSettings.timeFormat
};
return _.pick(uiSettings, [
'showRelativeDates',
'shortDateFormat',
'longDateFormat',
'timeFormat'
]);
}
);
}

View File

@@ -2,7 +2,7 @@ import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import HTML5Backend from 'react-dnd-html5-backend';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';

View File

@@ -156,35 +156,3 @@
.body {
padding: 5px;
}
.verticalContainer {
max-height: 300px;
}
.horizontalContainer {
max-width: calc($breakpointExtraSmall - 20px);
}
@media only screen and (min-width: $breakpointExtraSmall) {
.horizontalContainer {
max-width: calc($breakpointSmall * 0.8);
}
}
@media only screen and (min-width: $breakpointSmall) {
.horizontalContainer {
max-width: calc($breakpointMedium * 0.8);
}
}
@media only screen and (min-width: $breakpointMedium) {
.horizontalContainer {
max-width: calc($breakpointLarge * 0.8);
}
}
/* @media only screen and (max-width: $breakpointLarge) {
.horizontalContainer {
max-width: calc($breakpointLarge * 0.8);
}
} */

View File

@@ -4,26 +4,9 @@ import React, { Component } from 'react';
import { Manager, Popper, Reference } from 'react-popper';
import Portal from 'Components/Portal';
import { kinds, tooltipPositions } from 'Helpers/Props';
import dimensions from 'Styles/Variables/dimensions';
import { isMobile as isMobileUtil } from 'Utilities/mobile';
import styles from './Tooltip.css';
let maxWidth = null;
function getMaxWidth() {
const windowWidth = window.innerWidth;
if (windowWidth >= parseInt(dimensions.breakpointLarge)) {
maxWidth = 800;
} else if (windowWidth >= parseInt(dimensions.breakpointMedium)) {
maxWidth = 650;
} else if (windowWidth >= parseInt(dimensions.breakpointSmall)) {
maxWidth = 500;
} else {
maxWidth = 450;
}
}
class Tooltip extends Component {
//
@@ -34,7 +17,6 @@ class Tooltip extends Component {
this._scheduleUpdate = null;
this._closeTimeout = null;
this._maxWidth = maxWidth || getMaxWidth();
this.state = {
isOpen: false
@@ -72,11 +54,9 @@ class Tooltip extends Component {
} else if ((/^bottom/).test(data.placement)) {
data.styles.maxHeight = windowHeight - bottom - 20;
} else if ((/^right/).test(data.placement)) {
data.styles.maxWidth = Math.min(this._maxWidth, windowWidth - right - 20);
data.styles.maxHeight = top - 20;
data.styles.maxWidth = windowWidth - right - 50;
} else {
data.styles.maxWidth = Math.min(this._maxWidth, left - 20);
data.styles.maxHeight = top - 20;
data.styles.maxWidth = left - 35;
}
return data;
@@ -164,16 +144,10 @@ class Tooltip extends Component {
{({ ref, style, placement, arrowProps, scheduleUpdate }) => {
this._scheduleUpdate = scheduleUpdate;
const popperPlacement = placement ? placement.split('-')[0] : position;
const vertical = popperPlacement === 'top' || popperPlacement === 'bottom';
return (
<div
ref={ref}
className={classNames(
styles.tooltipContainer,
vertical ? styles.verticalContainer : styles.horizontalContainer
)}
className={styles.tooltipContainer}
style={style}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
@@ -182,7 +156,7 @@ class Tooltip extends Component {
className={this.state.isOpen ? classNames(
styles.arrow,
styles[kind],
styles[popperPlacement]
styles[placement.split('-')[0]]
) : styles.arrowDisabled}
ref={arrowProps.ref}
style={arrowProps.style}
@@ -227,7 +201,7 @@ Tooltip.defaultProps = {
bodyClassName: styles.body,
kind: kinds.DEFAULT,
position: tooltipPositions.TOP,
canFlip: false
canFlip: true
};
export default Tooltip;

View File

@@ -18,14 +18,11 @@ function createMapStateToProps() {
items
} = languages;
const filterItems = ['Any', 'Original'];
const filteredLanguages = items.filter((lang) => !filterItems.includes(lang.name));
return {
isFetching,
isPopulated,
error,
items: filteredLanguages
items
};
}
);
@@ -57,9 +54,7 @@ class SelectLanguageModalContentConnector extends Component {
const language = _.find(this.props.items,
(item) => item.id === parseInt(languageId));
if (language !== undefined) {
languages.push(language);
}
languages.push(language);
});
this.props.dispatchUpdateInteractiveImportItems({

View File

@@ -8,8 +8,8 @@ function createMapStateToProps() {
return createSelector(
(state, { guid }) => guid,
(state) => state.movieHistory.items,
(state) => state.movieBlacklist.items,
(guid, movieHistory, movieBlacklist) => {
(state) => state.blacklist.items,
(guid, movieHistory, blacklist) => {
let blacklistData = {};
let historyFailedData = {};
@@ -17,7 +17,7 @@ function createMapStateToProps() {
const historyGrabbedData = movieHistory.find((movie) => movie.eventType === 'grabbed' && movie.data.guid === guid);
if (historyGrabbedData) {
historyFailedData = movieHistory.find((movie) => movie.eventType === 'downloadFailed' && movie.sourceTitle === historyGrabbedData.sourceTitle);
blacklistData = movieBlacklist.find((item) => item.sourceTitle === historyGrabbedData.sourceTitle);
blacklistData = blacklist.find((item) => item.sourceTitle === historyGrabbedData.sourceTitle);
}
return {

View File

@@ -35,8 +35,8 @@ class DeleteMovieModalContentConnector extends Component {
this.props.onModalClose(true);
if (this.props.nextMovieRelativePath) {
this.props.push(window.Radarr.urlBase + this.props.nextMovieRelativePath);
if (this.props.previousMovie) {
this.props.push(this.props.previousMovie);
}
}
@@ -58,7 +58,7 @@ DeleteMovieModalContentConnector.propTypes = {
onModalClose: PropTypes.func.isRequired,
deleteMovie: PropTypes.func.isRequired,
push: PropTypes.func.isRequired,
nextMovieRelativePath: PropTypes.string
previousMovie: PropTypes.string
};
export default connect(createMapStateToProps, mapDispatchToProps)(DeleteMovieModalContentConnector);

View File

@@ -17,7 +17,6 @@ import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import Popover from 'Components/Tooltip/Popover';
import Tooltip from 'Components/Tooltip/Tooltip';
import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
import InteractiveSearchFilterMenuConnector from 'InteractiveSearch/InteractiveSearchFilterMenuConnector';
@@ -101,7 +100,6 @@ class MovieDetails extends Component {
window.removeEventListener('touchend', this.onTouchEnd);
window.removeEventListener('touchcancel', this.onTouchCancel);
window.removeEventListener('touchmove', this.onTouchMove);
window.removeEventListener('keyup', this.onKeyUp);
}
//
@@ -178,13 +176,11 @@ class MovieDetails extends Component {
}
onKeyUp = (event) => {
if (event.path.length === 4) {
if (event.keyCode === keyCodes.LEFT_ARROW) {
this.props.onGoToMovie(this.props.previousMovie.titleSlug);
}
if (event.keyCode === keyCodes.RIGHT_ARROW) {
this.props.onGoToMovie(this.props.nextMovie.titleSlug);
}
if (event.keyCode === keyCodes.LEFT_ARROW) {
this.props.onGoToMovie(this.props.previousMovie.titleSlug);
}
if (event.keyCode === keyCodes.RIGHT_ARROW) {
this.props.onGoToMovie(this.props.nextMovie.titleSlug);
}
}
@@ -240,10 +236,6 @@ class MovieDetails extends Component {
}
}
onTabSelect = (index, lastIndex) => {
this.setState({ selectedTabIndex: index });
}
//
// Render
@@ -462,14 +454,15 @@ class MovieDetails extends Component {
{
<span className={styles.links}>
<Tooltip
<Popover
anchor={
<Icon
name={icons.EXTERNAL_LINK}
size={20}
/>
}
tooltip={
title={translate('Links')}
body={
<MovieDetailsLinks
tmdbId={tmdbId}
imdbId={imdbId}
@@ -484,14 +477,15 @@ class MovieDetails extends Component {
{
!!tags.length &&
<span>
<Tooltip
<Popover
anchor={
<Icon
name={icons.TAGS}
size={20}
/>
}
tooltip={
title={translate('Tags')}
body={
<MovieTagsConnector movieId={id} />
}
position={tooltipPositions.BOTTOM}
@@ -619,7 +613,7 @@ class MovieDetails extends Component {
</div>
}
<Tabs selectedIndex={this.state.tabIndex} onSelect={this.onTabSelect}>
<Tabs selectedIndex={this.state.tabIndex} onSelect={(tabIndex) => this.setState({ selectedTabIndex: tabIndex })}>
<TabList
className={styles.tabList}
>
@@ -733,7 +727,7 @@ class MovieDetails extends Component {
isOpen={isDeleteMovieModalOpen}
movieId={id}
onModalClose={this.onDeleteMovieModalClose}
nextMovieRelativePath={`/movie/${nextMovie.titleSlug}`}
previousMovie={`/movie/${previousMovie.titleSlug}`}
/>
<InteractiveImportModal

View File

@@ -5,10 +5,10 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import * as commandNames from 'Commands/commandNames';
import { fetchBlacklist } from 'Store/Actions/blacklistActions';
import { executeCommand } from 'Store/Actions/commandActions';
import { clearExtraFiles, fetchExtraFiles } from 'Store/Actions/extraFileActions';
import { toggleMovieMonitored } from 'Store/Actions/movieActions';
import { clearMovieBlacklist, fetchMovieBlacklist } from 'Store/Actions/movieBlacklistActions';
import { clearMovieCredits, fetchMovieCredits } from 'Store/Actions/movieCreditsActions';
import { clearMovieFiles, fetchMovieFiles } from 'Store/Actions/movieFileActions';
import { clearMovieHistory, fetchMovieHistory } from 'Store/Actions/movieHistoryActions';
@@ -222,11 +222,9 @@ function createMapDispatchToProps(dispatch, props) {
onGoToMovie(titleSlug) {
dispatch(push(`${window.Radarr.urlBase}/movie/${titleSlug}`));
},
dispatchFetchMovieBlacklist({ movieId }) {
dispatch(fetchMovieBlacklist({ movieId }));
},
dispatchClearMovieBlacklist() {
dispatch(clearMovieBlacklist());
dispatchFetchBlacklist() {
// TODO: Allow for passing a movie id to fetch a single movie's blacklist data
dispatch(fetchBlacklist());
}
};
}
@@ -280,17 +278,20 @@ class MovieDetailsConnector extends Component {
const movieId = this.props.id;
this.props.dispatchFetchMovieFiles({ movieId });
this.props.dispatchFetchMovieBlacklist({ movieId });
this.props.dispatchFetchMovieHistory({ movieId });
this.props.dispatchFetchExtraFiles({ movieId });
this.props.dispatchFetchMovieCredits({ movieId });
this.props.dispatchFetchQueueDetails({ movieId });
this.props.dispatchFetchImportListSchema();
this.props.dispatchFetchBlacklist();
}
repopulate = () => {
this.props.dispatchFetchBlacklist();
}
unpopulate = () => {
this.props.dispatchCancelFetchReleases();
this.props.dispatchClearMovieBlacklist();
this.props.dispatchClearMovieFiles();
this.props.dispatchClearMovieHistory();
this.props.dispatchClearExtraFiles();
@@ -362,8 +363,7 @@ MovieDetailsConnector.propTypes = {
dispatchClearQueueDetails: PropTypes.func.isRequired,
dispatchFetchImportListSchema: PropTypes.func.isRequired,
dispatchExecuteCommand: PropTypes.func.isRequired,
dispatchFetchMovieBlacklist: PropTypes.func.isRequired,
dispatchClearMovieBlacklist: PropTypes.func.isRequired,
dispatchFetchBlacklist: PropTypes.func.isRequired,
onGoToMovie: PropTypes.func.isRequired
};

View File

@@ -1,3 +1,4 @@
import _ from 'lodash';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
@@ -7,12 +8,12 @@ function createMapStateToProps() {
return createSelector(
createUISettingsSelector(),
(uiSettings) => {
return {
showRelativeDates: uiSettings.showRelativeDates,
shortDateFormat: uiSettings.shortDateFormat,
longDateFormat: uiSettings.longDateFormat,
timeFormat: uiSettings.timeFormat
};
return _.pick(uiSettings, [
'showRelativeDates',
'shortDateFormat',
'longDateFormat',
'timeFormat'
]);
}
);
}

View File

@@ -1,3 +1,4 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@@ -35,13 +36,13 @@ function createMapStateToProps() {
pendingChanges
} = moviesState;
const movieSettings = {
monitored: movie.monitored,
qualityProfileId: movie.qualityProfileId,
minimumAvailability: movie.minimumAvailability,
path: movie.path,
tags: movie.tags
};
const movieSettings = _.pick(movie, [
'monitored',
'qualityProfileId',
'minimumAvailability',
'path',
'tags'
]);
const settings = selectSettings(movieSettings, pendingChanges, saveError);

View File

@@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
import React from 'react';
import getRelativeDate from 'Utilities/Date/getRelativeDate';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
import styles from './MovieIndexPosterInfo.css';
function MovieIndexPosterInfo(props) {
@@ -52,7 +51,7 @@ function MovieIndexPosterInfo(props) {
return (
<div className={styles.info}>
{translate('Added')}: {addedDate}
{`Added ${addedDate}`}
</div>
);
}
@@ -70,7 +69,7 @@ function MovieIndexPosterInfo(props) {
return (
<div className={styles.info}>
{translate('InCinemas')}: {inCinemasDate}
{`In Cinemas ${inCinemasDate}`}
</div>
);
}
@@ -88,7 +87,7 @@ function MovieIndexPosterInfo(props) {
return (
<div className={styles.info}>
{translate('Digital')}: {digitalReleaseDate}
{`Digital ${digitalReleaseDate}`}
</div>
);
}
@@ -106,7 +105,7 @@ function MovieIndexPosterInfo(props) {
return (
<div className={styles.info}>
{translate('Released')}: {physicalReleaseDate}
{`Released ${physicalReleaseDate}`}
</div>
);
}

View File

@@ -8,8 +8,8 @@ import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellCo
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
import TagListConnector from 'Components/TagListConnector';
import Tooltip from 'Components/Tooltip/Tooltip';
import { icons, kinds } from 'Helpers/Props';
import Popover from 'Components/Tooltip/Popover';
import { icons } from 'Helpers/Props';
import DeleteMovieModal from 'Movie/Delete/DeleteMovieModal';
import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks';
import EditMovieModalConnector from 'Movie/Edit/EditMovieModalConnector';
@@ -369,22 +369,21 @@ class MovieIndexRow extends Component {
className={styles[name]}
>
<span className={styles.externalLinks}>
<Tooltip
<Popover
anchor={
<Icon
name={icons.EXTERNAL_LINK}
size={12}
/>
}
tooltip={
title={translate('Links')}
body={
<MovieDetailsLinks
tmdbId={tmdbId}
imdbId={imdbId}
youTubeTrailerId={youTubeTrailerId}
/>
}
canFlip={true}
kind={kinds.INVERSE}
/>
</span>

View File

@@ -1,3 +1,4 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@@ -9,13 +10,16 @@ function createMapStateToProps() {
return createSelector(
createMovieSelector(),
(movie) => {
return {
inCinemas: movie.inCinemas,
isAvailable: movie.isAvailable,
monitored: movie.monitored,
grabbed: movie.grabbed,
movieFile: movie.movieFile
};
const result = _.pick(movie, [
'inCinemas',
'isAvailable',
'monitored',
'grabbed'
]);
result.movieFile = movie.movieFile;
return result;
}
);
}

View File

@@ -18,7 +18,7 @@ function createMapStateToProps() {
items
} = languages;
const filterItems = ['Any', 'Original'];
const filterItems = ['Any'];
const filteredLanguages = items.filter((lang) => !filterItems.includes(lang.name));
return {
@@ -57,9 +57,7 @@ class SelectLanguageModalContentConnector extends Component {
const language = _.find(this.props.items,
(item) => item.id === parseInt(languageId));
if (language !== undefined) {
languages.push(language);
}
languages.push(language);
});
this.props.dispatchupdateMovieFiles({

View File

@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import HTML5Backend from 'react-dnd-html5-backend';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';

View File

@@ -143,7 +143,7 @@ EditRemotePathMappingModalContent.propTypes = {
isSaving: PropTypes.bool.isRequired,
saveError: PropTypes.object,
item: PropTypes.shape(remotePathMappingShape).isRequired,
downloadClientHosts: PropTypes.arrayOf(PropTypes.object).isRequired,
downloadClientHosts: PropTypes.arrayOf(PropTypes.string).isRequired,
onInputChange: PropTypes.func.isRequired,
onSavePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired,

View File

@@ -36,7 +36,7 @@ function EditRestrictionModalContent(props) {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{id ? translate('EditRestriction') : translate('AddRestriction')}
{id ? 'Edit Restriction' : 'Add Restriction'}
</ModalHeader>
<ModalBody>
@@ -51,7 +51,7 @@ function EditRestrictionModalContent(props) {
name="required"
helpText={translate('RequiredHelpText')}
kind={kinds.SUCCESS}
placeholder={translate('RequiredRestrictionPlaceHolder')}
placeholder={translate('RequiredPlaceHolder')}
{...required}
onChange={onInputChange}
/>

View File

@@ -116,10 +116,8 @@ class NamingModal extends Component {
const movieTokens = [
{ token: '{Movie Title}', example: 'Movie Title!' },
{ token: '{Movie Title:DE}', example: 'Filetitle' },
{ token: '{Movie CleanTitle}', example: 'Movie Title' },
{ token: '{Movie TitleThe}', example: 'Movie Title, The' },
{ token: '{Movie OriginalTitle}', example: 'Τίτλος ταινίας' },
{ token: '{Movie TitleFirstCharacter}', example: 'M' },
{ token: '{Movie Collection}', example: 'The Movie Collection' },
{ token: '{Movie Certification}', example: 'R' },

View File

@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import HTML5Backend from 'react-dnd-html5-backend';
import Link from 'Components/Link/Link';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';

View File

@@ -3,8 +3,7 @@
flex-wrap: wrap;
}
.formGroupWrapper,
.formatItemLarge {
.formGroupWrapper {
flex: 0 0 calc($formGroupSmallWidth - 100px);
}
@@ -12,20 +11,8 @@
margin-right: auto;
}
.formatItemSmall {
display: none;
}
@media only screen and (max-width: calc($breakpointLarge + 100px)) {
@media only screen and (max-width: $breakpointLarge) {
.formGroupsContainer {
display: block;
}
.formatItemSmall {
display: block;
}
.formatItemLarge {
display: none;
}
}

View File

@@ -21,17 +21,6 @@ import styles from './EditQualityProfileModalContent.css';
const MODAL_BODY_PADDING = parseInt(dimensions.modalBodyPadding);
function getCustomFormatRender(formatItems, otherProps) {
return (
<QualityProfileFormatItems
profileFormatItems={formatItems.value}
errors={formatItems.errors}
warnings={formatItems.warnings}
{...otherProps}
/>
);
}
class EditQualityProfileModalContent extends Component {
//
@@ -262,10 +251,6 @@ class EditQualityProfileModalContent extends Component {
onChange={onLanguageChange}
/>
</FormGroup>
<div className={styles.formatItemLarge}>
{getCustomFormatRender(formatItems, ...otherProps)}
</div>
</div>
<div className={styles.formGroupWrapper}>
@@ -278,8 +263,13 @@ class EditQualityProfileModalContent extends Component {
/>
</div>
<div className={styles.formatItemSmall}>
{getCustomFormatRender(formatItems, otherProps)}
<div className={styles.formGroupWrapper}>
<QualityProfileFormatItems
profileFormatItems={formatItems.value}
errors={formatItems.errors}
warnings={formatItems.warnings}
{...otherProps}
/>
</div>
</div>
</Form>

View File

@@ -65,8 +65,6 @@ class UISettings extends Component {
...otherProps
} = this.props;
const uiLanguages = languages.filter((item) => item.value !== 'Original');
return (
<PageContent title={translate('UISettings')}>
<SettingsToolbarConnector
@@ -215,7 +213,7 @@ class UISettings extends Component {
<FormInputGroup
type={inputTypes.SELECT}
name="uiLanguage"
values={uiLanguages}
values={languages}
helpText={translate('UILanguageHelpText')}
helpTextWarning={translate('UILanguageHelpTextWarning')}
onChange={onInputChange}

View File

@@ -11,7 +11,6 @@ import * as history from './historyActions';
import * as importMovie from './importMovieActions';
import * as interactiveImportActions from './interactiveImportActions';
import * as movies from './movieActions';
import * as movieBlacklist from './movieBlacklistActions';
import * as movieCredits from './movieCreditsActions';
import * as movieFiles from './movieFileActions';
import * as movieHistory from './movieHistoryActions';
@@ -49,7 +48,6 @@ export default [
releases,
rootFolders,
movies,
movieBlacklist,
movieHistory,
movieIndex,
movieCredits,

View File

@@ -1,82 +0,0 @@
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import { createThunk, handleThunks } from 'Store/thunks';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import { set, update } from './baseActions';
import createHandleActions from './Creators/createHandleActions';
//
// Variables
export const section = 'movieBlacklist';
//
// State
export const defaultState = {
isFetching: false,
isPopulated: false,
error: null,
items: []
};
//
// Actions Types
export const FETCH_MOVIE_BLACKLIST = 'movieBlacklist/fetchMovieBlacklist';
export const CLEAR_MOVIE_BLACKLIST = 'movieBlacklist/clearMovieBlacklist';
//
// Action Creators
export const fetchMovieBlacklist = createThunk(FETCH_MOVIE_BLACKLIST);
export const clearMovieBlacklist = createAction(CLEAR_MOVIE_BLACKLIST);
//
// Action Handlers
export const actionHandlers = handleThunks({
[FETCH_MOVIE_BLACKLIST]: function(getState, payload, dispatch) {
dispatch(set({ section, isFetching: true }));
const promise = createAjaxRequest({
url: '/blacklist/movie',
data: payload
}).request;
promise.done((data) => {
dispatch(batchActions([
update({ section, data }),
set({
section,
isFetching: false,
isPopulated: true,
error: null
})
]));
});
promise.fail((xhr) => {
dispatch(set({
section,
isFetching: false,
isPopulated: false,
error: xhr
}));
});
}
});
//
// Reducers
export const reducers = createHandleActions({
[CLEAR_MOVIE_BLACKLIST]: (state) => {
return Object.assign({}, state, defaultState);
}
}, defaultState, section);

View File

@@ -1,5 +1,4 @@
import _ from 'lodash';
import moment from 'moment';
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import { sortDirections } from 'Helpers/Props';
@@ -143,11 +142,6 @@ export const defaultState = {
isModifiable: false
}
]
},
sortPredicates: {
estimatedCompletionTime: function(item, direction) {
return moment.duration(item.timeleft).asMilliseconds();
}
}
};

View File

@@ -22,7 +22,7 @@ class MoreInfo extends Component {
<DescriptionListItemTitle>Discord</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://discord.gg/r5wJPt9">discord.gg/r5wJPt9</Link>
<Link to="https://discord.gg/AD3UP37">discord.gg/AD3UP37</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Wiki</DescriptionListItemTitle>

View File

@@ -24,13 +24,9 @@ class UpdateChanges extends Component {
<ul>
{
changes.map((change, index) => {
const checkChange = change.replace(/#\d{4,5}\b/g, (match, contents) => {
return `[${match}](https://github.com/Radarr/Radarr/issues/${match.substring(1)})`;
});
return (
<li key={index}>
<InlineMarkdown data={checkChange} />
<InlineMarkdown data={change} />
</li>
);
})

View File

@@ -7,8 +7,7 @@ function formatTimeSpan(timeSpan) {
}
const duration = moment.duration(timeSpan);
const days = Math.floor(duration.asDays());
const days = duration.get('days');
const hours = padNumber(duration.get('hours'), 2);
const minutes = padNumber(duration.get('minutes'), 2);
const seconds = padNumber(duration.get('seconds'), 2);

View File

@@ -1,3 +1,4 @@
import _ from 'lodash';
import qs from 'qs';
// See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils
@@ -9,17 +10,18 @@ export default function parseUrl(url) {
// The `origin`, `password`, and `username` properties are unavailable in
// Opera Presto. We synthesize `origin` if it's not present. While `password`
// and `username` are ignored intentionally.
const properties = {
hash: anchor.hash,
host: anchor.host,
hostname: anchor.hostname,
href: anchor.href,
origin: anchor.origin,
pathname: anchor.pathname,
port: anchor.port,
protocol: anchor.protocol,
search: anchor.search
};
const properties = _.pick(
anchor,
'hash',
'host',
'hostname',
'href',
'origin',
'pathname',
'port',
'protocol',
'search'
);
properties.isAbsolute = (/^[\w:]*\/\//).test(url);

62
macOS/Radarr Normal file
View File

@@ -0,0 +1,62 @@
#!/bin/sh
#get the bundle's MacOS directory full path
DIR=$(cd "$(dirname "$0")"; pwd)
#change these values to match your app
EXE_PATH="$DIR/Radarr.exe"
APPNAME="Radarr"
#set up environment
if [[ -x '/opt/local/bin/mono' ]]; then
# Macports and mono-supplied installer path
export PATH="/opt/local/bin:$PATH"
elif [[ -x '/usr/local/bin/mono' ]]; then
# Homebrew-supplied path to mono
export PATH="/usr/local/bin:$PATH"
fi
export DYLD_FALLBACK_LIBRARY_PATH="$DIR"
if [ -e /Library/Frameworks/Mono.framework ]; then
MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
export PATH="$MONO_FRAMEWORK_PATH/bin:$PATH"
export DYLD_FALLBACK_LIBRARY_PATH="$DYLD_FALLBACK_LIBRARY_PATH:$MONO_FRAMEWORK_PATH/lib"
fi
if [[ -f '/opt/local/lib/libsqlite3.0.dylib' ]]; then
export DYLD_FALLBACK_LIBRARY_PATH="/opt/local/lib:$DYLD_FALLBACK_LIBRARY_PATH"
fi
export DYLD_FALLBACK_LIBRARY_PATH="$DYLD_FALLBACK_LIBRARY_PATH:$HOME/lib:/usr/local/lib:/lib:/usr/lib"
#mono version check
REQUIRED_MAJOR=4
REQUIRED_MINOR=6
VERSION_TITLE="Cannot launch $APPNAME"
VERSION_MSG="$APPNAME requires Mono Runtime Environment(MRE) $REQUIRED_MAJOR.$REQUIRED_MINOR or later."
DOWNLOAD_URL="http://www.mono-project.com/download/#download-mac"
MONO_VERSION="$(mono --version | grep 'Mono JIT compiler version ' | cut -f5 -d\ )"
# if [[ -o DEBUG ]]; then osascript -e "display dialog \"MONO_VERSION: $MONO_VERSION\""; fi
MONO_VERSION_MAJOR="$(echo $MONO_VERSION | cut -f1 -d.)"
MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
if [ -z "$MONO_VERSION" ] \
|| [ $MONO_VERSION_MAJOR -lt $REQUIRED_MAJOR ] \
|| [ $MONO_VERSION_MAJOR -eq $REQUIRED_MAJOR -a $MONO_VERSION_MINOR -lt $REQUIRED_MINOR ]
then
osascript \
-e "set question to display dialog \"$VERSION_MSG\" with title \"$VERSION_TITLE\" buttons {\"Cancel\", \"Download...\"} default button 2" \
-e "if button returned of question is equal to \"Download...\" then open location \"$DOWNLOAD_URL\""
echo "$VERSION_TITLE"
echo "$VERSION_MSG"
exit 1
fi
MONO_EXEC="exec mono --debug"
#run app using mono
$MONO_EXEC "$EXE_PATH"

View File

@@ -17,27 +17,28 @@
"license": "GPL-3.0",
"readmeFilename": "readme.md",
"dependencies": {
"@babel/core": "7.11.6",
"@babel/plugin-proposal-class-properties": "7.10.4",
"@babel/plugin-proposal-decorators": "7.10.5",
"@babel/plugin-proposal-export-default-from": "7.10.4",
"@babel/plugin-proposal-export-namespace-from": "7.10.4",
"@babel/plugin-proposal-function-sent": "7.10.4",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.10.4",
"@babel/plugin-proposal-numeric-separator": "7.10.4",
"@babel/plugin-proposal-optional-chaining": "7.11.0",
"@babel/plugin-proposal-throw-expressions": "7.10.4",
"@babel/core": "7.9.6",
"@babel/plugin-proposal-class-properties": "7.8.3",
"@babel/plugin-proposal-decorators": "7.8.3",
"@babel/plugin-proposal-export-default-from": "7.8.3",
"@babel/plugin-proposal-export-namespace-from": "7.8.3",
"@babel/plugin-proposal-function-sent": "7.8.3",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.8.3",
"@babel/plugin-proposal-numeric-separator": "7.8.3",
"@babel/plugin-proposal-optional-chaining": "7.9.0",
"@babel/plugin-proposal-throw-expressions": "7.8.3",
"@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/preset-env": "7.11.5",
"@babel/preset-react": "7.10.4",
"@fortawesome/fontawesome-free": "5.15.0",
"@fortawesome/fontawesome-svg-core": "1.2.31",
"@fortawesome/free-regular-svg-icons": "5.15.0",
"@fortawesome/free-solid-svg-icons": "5.15.0",
"@fortawesome/react-fontawesome": "0.1.11",
"@babel/preset-env": "7.9.6",
"@babel/preset-react": "7.9.4",
"@fortawesome/fontawesome-free": "5.13.0",
"@fortawesome/fontawesome-svg-core": "1.2.28",
"@fortawesome/free-regular-svg-icons": "5.13.0",
"@fortawesome/free-solid-svg-icons": "5.13.0",
"@fortawesome/react-fontawesome": "0.1.9",
"@microsoft/signalr": "3.1.7",
"@sentry/browser": "5.24.2",
"@sentry/integrations": "5.24.2",
"@popperjs/core": "2.2.1",
"@sentry/browser": "5.15.5",
"@sentry/integrations": "5.15.5",
"ansi-colors": "4.1.1",
"autoprefixer": "9.7.5",
"babel-eslint": "10.1.0",
@@ -47,19 +48,19 @@
"classnames": "2.2.6",
"clipboard": "2.0.6",
"connected-react-router": "6.8.0",
"core-js": "3.6.5",
"core-js": "3",
"css-loader": "3.4.2",
"del": "6.0.0",
"del": "5.1.0",
"element-class": "0.2.2",
"eslint": "7.10.0",
"eslint": "7.5.0",
"eslint-plugin-filenames": "1.3.2",
"eslint-plugin-import": "2.22.0",
"eslint-plugin-react": "7.21.3",
"eslint-plugin-react": "7.20.5",
"eslint-plugin-simple-import-sort": "5.0.3",
"esprint": "0.7.0",
"file-loader": "6.1.0",
"file-loader": "6.0.0",
"filesize": "6.1.0",
"fuse.js": "6.4.1",
"fuse.js": "6.0.4",
"gulp": "4.0.2",
"gulp-cached": "1.1.1",
"gulp-concat": "2.6.1",
@@ -70,14 +71,14 @@
"gulp-watch": "5.0.1",
"gulp-wrap": "0.15.0",
"history": "4.10.1",
"html-webpack-plugin": "4.5.0",
"html-webpack-plugin": "3.2.0",
"jdu": "1.0.0",
"jquery": "3.5.1",
"loader-utils": "^2.0.0",
"lodash": "4.17.20",
"mini-css-extract-plugin": "0.9.0",
"mobile-detect": "1.4.4",
"moment": "2.29.0",
"moment": "2.24.0",
"mousetrap": "1.6.5",
"normalize.css": "8.0.1",
"optimize-css-assets-webpack-plugin": "5.0.3",
@@ -91,22 +92,22 @@
"qs": "6.9.4",
"react": "16.13.1",
"react-addons-shallow-compare": "15.6.2",
"react-async-script": "1.2.0",
"react-async-script": "1.1.1",
"react-autosuggest": "10.0.2",
"react-custom-scrollbars": "4.2.1",
"react-dnd": "11.1.3",
"react-dnd-html5-backend": "11.1.3",
"react-dnd": "10.0.2",
"react-dnd-html5-backend": "10.0.2",
"react-document-title": "2.0.3",
"react-dom": "16.13.1",
"react-focus-lock": "2.4.1",
"react-google-recaptcha": "2.1.0",
"react-lazyload": "3.0.0",
"react-focus-lock": "2.3.1",
"react-google-recaptcha": "2.0.1",
"react-lazyload": "2.6.7",
"react-measure": "1.4.7",
"react-popper": "1.3.7",
"react-redux": "7.2.1",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
"react-slider": "1.0.11",
"react-redux": "7.2.0",
"react-router": "5.1.2",
"react-router-dom": "5.1.2",
"react-slider": "1.0.7",
"react-tabs": "3.1.1",
"react-text-truncate": "0.16.0",
"react-virtualized": "9.21.1",
@@ -120,12 +121,12 @@
"run-sequence": "2.2.1",
"streamqueue": "1.1.2",
"style-loader": "1.2.1",
"stylelint": "13.7.2",
"stylelint": "13.6.1",
"stylelint-order": "4.1.0",
"url-loader": "4.1.0",
"webpack": "4.44.2",
"webpack-stream": "6.1.0",
"worker-loader": "3.0.3"
"webpack": "4.42.1",
"webpack-stream": "5.2.1",
"worker-loader": "2.0.0"
},
"main": "index.js",
"browserslist": [

View File

@@ -1,4 +1,3 @@
using System;
using System.IO;
using System.Linq;
using Nancy;
@@ -58,7 +57,7 @@ namespace NzbDrone.Api.FileSystem
if (!_diskProvider.FolderExists(path))
{
return Array.Empty<string>();
return new string[0];
}
return _diskScanService.GetVideoFiles(path).Select(f => new

View File

@@ -56,7 +56,9 @@ namespace NzbDrone.Api.Indexers
public int? Leechers { get; set; }
public DownloadProtocol Protocol { get; set; }
// JsonIgnore so we don't serialize it, but can still parse it, removed in v3
// TODO: Remove in v3
// Used to support the original Release Push implementation
// JsonIgnore so we don't serialize it, but can still parse it
[JsonIgnore]
public DownloadProtocol DownloadProtocol
{

View File

@@ -147,7 +147,7 @@ namespace NzbDrone.Api
{
var defaultDefinitions = _providerFactory.GetDefaultDefinitions().OrderBy(p => p.ImplementationName).ToList();
var result = new List<TProviderResource>(defaultDefinitions.Count);
var result = new List<TProviderResource>(defaultDefinitions.Count());
foreach (var providerDefinition in defaultDefinitions)
{

View File

@@ -731,7 +731,7 @@ namespace NzbDrone.Common.Test.DiskTests
// Note: never returns anything.
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetFileInfos(It.IsAny<string>(), SearchOption.TopDirectoryOnly))
.Setup(v => v.GetFileInfos(It.IsAny<string>()))
.Returns(new List<FileInfo>());
Mocker.GetMock<IDiskProvider>()
@@ -765,8 +765,8 @@ namespace NzbDrone.Common.Test.DiskTests
.Returns<string>(v => new DirectoryInfo(v).GetDirectories().ToList());
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetFileInfos(It.IsAny<string>(), SearchOption.TopDirectoryOnly))
.Returns<string, SearchOption>((v, _) => new DirectoryInfo(v).GetFiles().ToList());
.Setup(v => v.GetFileInfos(It.IsAny<string>()))
.Returns<string>(v => new DirectoryInfo(v).GetFiles().ToList());
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetFileSize(It.IsAny<string>()))

View File

@@ -1,4 +1,3 @@
using System;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
@@ -12,7 +11,7 @@ namespace NzbDrone.Common.Test.EnvironmentTests
[Test]
public void empty_array_should_return_empty_flags()
{
var args = new StartupContext(Array.Empty<string>());
var args = new StartupContext(new string[0]);
args.Flags.Should().BeEmpty();
}

View File

@@ -46,7 +46,7 @@ namespace NzbDrone.Common.Test.Http
TestLogger.Info($"{candidates.Length} TestSites available.");
_httpBinSleep = _httpBinHosts.Length < 2 ? 100 : 10;
_httpBinSleep = _httpBinHosts.Count() < 2 ? 100 : 10;
}
private bool IsTestSiteAvailable(string site)
@@ -100,7 +100,7 @@ namespace NzbDrone.Common.Test.Http
Mocker.SetConstant<ICacheManager>(Mocker.Resolve<CacheManager>());
Mocker.SetConstant<ICreateManagedWebProxy>(Mocker.Resolve<ManagedWebProxyFactory>());
Mocker.SetConstant<IRateLimitService>(Mocker.Resolve<RateLimitService>());
Mocker.SetConstant<IEnumerable<IHttpRequestInterceptor>>(Array.Empty<IHttpRequestInterceptor>());
Mocker.SetConstant<IEnumerable<IHttpRequestInterceptor>>(new IHttpRequestInterceptor[0]);
Mocker.SetConstant<IHttpDispatcher>(Mocker.Resolve<TDispatcher>());
// Used for manual testing of socks proxies.
@@ -352,7 +352,7 @@ namespace NzbDrone.Common.Test.Http
var oldRequest = new HttpRequest($"https://{_httpBinHost2}/get");
oldRequest.Cookies["my"] = "cookie";
var oldClient = new HttpClient(Array.Empty<IHttpRequestInterceptor>(), Mocker.Resolve<ICacheManager>(), Mocker.Resolve<IRateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.Resolve<Logger>());
var oldClient = new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<ICacheManager>(), Mocker.Resolve<IRateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.Resolve<Logger>());
oldClient.Should().NotBeSameAs(Subject);

View File

@@ -505,20 +505,13 @@ namespace NzbDrone.Common.Disk
return di.GetDirectories().ToList();
}
public FileInfo GetFileInfo(string path)
{
Ensure.That(path, () => path).IsValidPath();
return new FileInfo(path);
}
public List<FileInfo> GetFileInfos(string path, SearchOption searchOption = SearchOption.TopDirectoryOnly)
public List<FileInfo> GetFileInfos(string path)
{
Ensure.That(path, () => path).IsValidPath();
var di = new DirectoryInfo(path);
return di.GetFiles("*", searchOption).ToList();
return di.GetFiles().ToList();
}
public void RemoveEmptySubfolders(string path)

View File

@@ -52,8 +52,7 @@ namespace NzbDrone.Common.Disk
List<IMount> GetMounts();
IMount GetMount(string path);
List<DirectoryInfo> GetDirectoryInfos(string path);
FileInfo GetFileInfo(string path);
List<FileInfo> GetFileInfos(string path, SearchOption searchOption = SearchOption.TopDirectoryOnly);
List<FileInfo> GetFileInfos(string path);
void RemoveEmptySubfolders(string path);
void SaveStream(Stream stream, string path);
bool IsValidFilePermissionMask(string mask);

View File

@@ -56,7 +56,7 @@ namespace NzbDrone.Common.Http
public string[] GetCookieHeaders()
{
return Headers.GetValues("Set-Cookie") ?? Array.Empty<string>();
return Headers.GetValues("Set-Cookie") ?? new string[0];
}
public Dictionary<string, string> GetCookies()

View File

@@ -27,7 +27,7 @@ namespace NzbDrone.Common.Http
if (scheme.IsNotNullOrWhiteSpace())
{
builder.Append(scheme);
builder.Append(':');
builder.Append(":");
}
if (host.IsNotNullOrWhiteSpace())
@@ -36,7 +36,7 @@ namespace NzbDrone.Common.Http
builder.Append(host);
if (port.HasValue)
{
builder.Append(':');
builder.Append(":");
builder.Append(port);
}
}
@@ -200,11 +200,11 @@ namespace NzbDrone.Common.Http
{
if (builder.Length != 0)
{
builder.Append('&');
builder.Append("&");
}
builder.Append(Uri.EscapeDataString(pair.Key));
builder.Append('=');
builder.Append("=");
builder.Append(Uri.EscapeDataString(pair.Value));
}

View File

@@ -1,5 +1,4 @@
using System;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Http.Proxy
{
@@ -42,7 +41,7 @@ namespace NzbDrone.Common.Http.Proxy
return hostlist;
}
return Array.Empty<string>();
return new string[] { };
}
}

View File

@@ -12,7 +12,7 @@ namespace NzbDrone.Common.Instrumentation
{
public static class NzbDroneLogger
{
private const string FILE_LOG_LAYOUT = @"${date:format=yyyy-M-d HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
private const string FILE_LOG_LAYOUT = @"${date:format=yyyy-M-d HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}}";
private static bool _isConfigured;
@@ -94,7 +94,7 @@ namespace NzbDrone.Common.Instrumentation
{
DebuggerTarget target = new DebuggerTarget();
target.Name = "debuggerLogger";
target.Layout = "[${level}] [${threadid}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
target.Layout = "[${level}] [${threadid}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}}";
var loggingRule = new LoggingRule("*", LogLevel.Trace, target);
LogManager.Configuration.AddTarget("debugger", target);
@@ -108,7 +108,7 @@ namespace NzbDrone.Common.Instrumentation
var coloredConsoleTarget = new ColoredConsoleTarget();
coloredConsoleTarget.Name = "consoleLogger";
coloredConsoleTarget.Layout = @"${date:format=yyyy-M-d HH\:mm\:ss.ff} | [${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
coloredConsoleTarget.Layout = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}}";
var loggingRule = new LoggingRule("*", level, coloredConsoleTarget);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
@@ -488,7 +488,7 @@ namespace NzbDrone.Common.OAuth
private static bool IsNullOrBlank(string value)
{
return string.IsNullOrEmpty(value) || (!string.IsNullOrEmpty(value) && string.IsNullOrEmpty(value.Trim()));
return string.IsNullOrEmpty(value) || (!string.IsNullOrEmpty(value) && value.Trim() == string.Empty);
}
}
}

View File

@@ -405,7 +405,7 @@ namespace NzbDrone.Common.OAuth
private static bool IsNullOrBlank(string value)
{
return string.IsNullOrEmpty(value) || (!string.IsNullOrEmpty(value) && string.IsNullOrEmpty(value.Trim()));
return string.IsNullOrEmpty(value) || (!string.IsNullOrEmpty(value) && value.Trim() == string.Empty);
}
}
}

View File

@@ -16,7 +16,7 @@ namespace NzbDrone.Common.OAuth
{
var parameters = this.Where(p => p.Name.Equals(name));
if (!parameters.Any())
if (parameters.Count() == 0)
{
return null;
}

View File

@@ -1,4 +1,4 @@
//===============================================================================
//===============================================================================
// TinyIoC
//
// An easy to use, hassle free, Inversion of Control Container for small projects
@@ -298,11 +298,11 @@ namespace TinyIoC
}
catch (System.IO.FileNotFoundException)
{
assemblies = Array.Empty<Type>();
assemblies = new Type[] { };
}
catch (NotSupportedException)
{
assemblies = Array.Empty<Type>();
assemblies = new Type[] { };
}
#if !NETFX_CORE
catch (ReflectionTypeLoadException e)
@@ -3355,7 +3355,7 @@ namespace TinyIoC
//#if NETFX_CORE
// MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => !mi.GetParameters().Any());
//#else
MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", Array.Empty<Type>());
MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { });
//#endif
resolveMethod = resolveMethod.MakeGenericMethod(returnType);
@@ -3488,7 +3488,7 @@ namespace TinyIoC
//#if NETFX_CORE
// return type.GetTypeInfo().DeclaredConstructors.OrderByDescending(ctor => ctor.GetParameters().Count());
//#else
return type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Length);
return type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Count());
//#endif
}
@@ -3534,9 +3534,9 @@ namespace TinyIoC
throw new TinyIoCResolutionException(typeToConstruct);
var ctorParams = constructor.GetParameters();
object[] args = new object[ctorParams.Length];
object[] args = new object[ctorParams.Count()];
for (int parameterIndex = 0; parameterIndex < ctorParams.Length; parameterIndex++)
for (int parameterIndex = 0; parameterIndex < ctorParams.Count(); parameterIndex++)
{
var currentParam = ctorParams[parameterIndex];
@@ -3648,7 +3648,7 @@ namespace TinyIoC
private IEnumerable<TypeRegistration> GetParentRegistrationsForType(Type resolveType)
{
if (_Parent == null)
return Array.Empty<TypeRegistration>();
return new TypeRegistration[] { };
var registrations = _Parent._RegisteredTypes.Keys.Where(tr => tr.Type == resolveType);
@@ -3660,7 +3660,7 @@ namespace TinyIoC
var registrations = _RegisteredTypes.Keys.Where(tr => tr.Type == resolveType).Concat(GetParentRegistrationsForType(resolveType));
if (!includeUnnamed)
registrations = registrations.Where(tr => !string.IsNullOrEmpty(tr.Name));
registrations = registrations.Where(tr => tr.Name != string.Empty);
return registrations.Select(registration => this.ResolveInternal(registration, NamedParameterOverloads.Default, ResolveOptions.Default));
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -81,8 +81,8 @@ namespace NzbDrone.Core.Test.Blacklisting
Subject.DeleteForMovies(new List<int> { _movie1.Id });
var removedMovieBlacklists = Subject.BlacklistedByMovie(_movie1.Id);
var nonRemovedMovieBlacklists = Subject.BlacklistedByMovie(_movie2.Id);
var removedMovieBlacklists = Subject.BlacklistedByMovies(new List<int> { _movie1.Id });
var nonRemovedMovieBlacklists = Subject.BlacklistedByMovies(new List<int> { _movie2.Id });
removedMovieBlacklists.Should().HaveCount(0);
nonRemovedMovieBlacklists.Should().HaveCount(1);

View File

@@ -30,8 +30,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Profile = new Profile
{
Language = Language.English
},
OriginalLanguage = Language.French
}
}
};
}
@@ -46,11 +45,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_remoteMovie.ParsedMovieInfo.Languages = new List<Language> { Language.German };
}
private void WithFrenchRelease()
{
_remoteMovie.ParsedMovieInfo.Languages = new List<Language> { Language.French };
}
[Test]
public void should_return_true_if_language_is_english()
{
@@ -67,26 +61,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
}
[Test]
public void should_return_false_if_release_is_german_and_profile_original()
{
_remoteMovie.Movie.Profile.Language = Language.Original;
WithGermanRelease();
Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
}
[Test]
public void should_return_true_if_release_is_french_and_profile_original()
{
_remoteMovie.Movie.Profile.Language = Language.Original;
WithFrenchRelease();
Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_allowed_language_any()
{

View File

@@ -89,7 +89,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DelugeTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>()));
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
}
protected void GivenFailedDownload()

View File

@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>()));
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
Mocker.GetMock<IRemotePathMappingService>()
.Setup(v => v.RemapRemoteToLocal(It.IsAny<string>(), It.IsAny<OsPath>()))

View File

@@ -278,7 +278,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>()));
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
_downloadStationConfigItems = new Dictionary<string, object>
{

View File

@@ -170,7 +170,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>()));
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
_downloadStationConfigItems = new Dictionary<string, object>
{

View File

@@ -86,7 +86,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>()));
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
}
protected void GivenFailedDownload()

View File

@@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>()));
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
Mocker.GetMock<IQBittorrentProxy>()
.Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>()))
@@ -53,7 +53,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, httpHeader, Array.Empty<byte>(), System.Net.HttpStatusCode.SeeOther));
.Returns<HttpRequest>(r => new HttpResponse(r, httpHeader, new byte[0], System.Net.HttpStatusCode.SeeOther));
}
protected void GivenRedirectToTorrent()
@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.Is<HttpRequest>(h => h.Url.FullUri == _downloadUrl)))
.Returns<HttpRequest>(r => new HttpResponse(r, httpHeader, Array.Empty<byte>(), System.Net.HttpStatusCode.Found));
.Returns<HttpRequest>(r => new HttpResponse(r, httpHeader, new byte[0], System.Net.HttpStatusCode.Found));
}
protected void GivenFailedDownload()
@@ -355,7 +355,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
Mocker.GetMock<IQBittorrentProxy>()
.Setup(v => v.MoveTorrentToTopInQueue(It.IsAny<string>(), It.IsAny<QBittorrentSettings>()))
.Throws(new HttpException(new HttpResponse(new HttpRequest("http://me.local/"), new HttpHeader(), Array.Empty<byte>(), System.Net.HttpStatusCode.Forbidden)));
.Throws(new HttpException(new HttpResponse(new HttpRequest("http://me.local/"), new HttpHeader(), new byte[0], System.Net.HttpStatusCode.Forbidden)));
var remoteMovie = CreateRemoteMovie();

View File

@@ -216,6 +216,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
[TestCase(SabnzbdDownloadStatus.Checking)]
[TestCase(SabnzbdDownloadStatus.Downloading)]
[TestCase(SabnzbdDownloadStatus.QuickCheck)]
[TestCase(SabnzbdDownloadStatus.ToPP)]
[TestCase(SabnzbdDownloadStatus.Verifying)]
[TestCase(SabnzbdDownloadStatus.Repairing)]
[TestCase(SabnzbdDownloadStatus.Fetching)]
@@ -536,7 +537,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
public void should_test_failed_if_tv_sorting_empty()
{
_config.Misc.enable_tv_sorting = true;
_config.Misc.tv_categories = Array.Empty<string>();
_config.Misc.tv_categories = new string[0];
var result = new NzbDroneValidationResult(Subject.Test());

View File

@@ -99,7 +99,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>()));
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
_transmissionConfigItems = new Dictionary<string, object>();

View File

@@ -91,7 +91,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>()));
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
}
protected void GivenRedirectToMagnet()
@@ -101,7 +101,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, httpHeader, Array.Empty<byte>(), System.Net.HttpStatusCode.SeeOther));
.Returns<HttpRequest>(r => new HttpResponse(r, httpHeader, new byte[0], System.Net.HttpStatusCode.SeeOther));
}
protected void GivenRedirectToTorrent()
@@ -111,7 +111,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.Is<HttpRequest>(h => h.Url.ToString() == _downloadUrl)))
.Returns<HttpRequest>(r => new HttpResponse(r, httpHeader, Array.Empty<byte>(), System.Net.HttpStatusCode.Found));
.Returns<HttpRequest>(r => new HttpResponse(r, httpHeader, new byte[0], System.Net.HttpStatusCode.Found));
}
protected void GivenFailedDownload()

View File

@@ -123,7 +123,7 @@ namespace NzbDrone.Core.Test.Download
public void Download_report_should_trigger_indexer_backoff_on_http429_with_long_time()
{
var request = new HttpRequest("http://my.indexer.com");
var response = new HttpResponse(request, new HttpHeader(), Array.Empty<byte>(), (HttpStatusCode)429);
var response = new HttpResponse(request, new HttpHeader(), new byte[0], (HttpStatusCode)429);
response.Headers["Retry-After"] = "300";
var mock = WithUsenetClient();
@@ -143,7 +143,7 @@ namespace NzbDrone.Core.Test.Download
public void Download_report_should_trigger_indexer_backoff_on_http429_based_on_date()
{
var request = new HttpRequest("http://my.indexer.com");
var response = new HttpResponse(request, new HttpHeader(), Array.Empty<byte>(), (HttpStatusCode)429);
var response = new HttpResponse(request, new HttpHeader(), new byte[0], (HttpStatusCode)429);
response.Headers["Retry-After"] = DateTime.UtcNow.AddSeconds(300).ToString("r");
var mock = WithUsenetClient();

View File

@@ -26,7 +26,7 @@ namespace NzbDrone.Core.Test.Framework
Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>()));
Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>()));
Mocker.SetConstant<IHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<IPlatformInfo>(), TestLogger));
Mocker.SetConstant<IHttpClient>(new HttpClient(Array.Empty<IHttpRequestInterceptor>(), Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), TestLogger));
Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), TestLogger));
Mocker.SetConstant<IRadarrCloudRequestBuilder>(new RadarrCloudRequestBuilder());
}

View File

@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
{
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Returns(Array.Empty<IDownloadClient>());
.Returns(new IDownloadClient[0]);
Subject.Check().ShouldBeWarning();
}

View File

@@ -34,6 +34,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
}
[TestCase("develop")]
[TestCase("v0.2")]
public void should_return_error_when_branch_is_v1(string branch)
{
GivenValidBranch(branch);

View File

@@ -42,8 +42,8 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
torrentInfo.Title.Should().Be("Storming.Juno.2010.1080p.BluRay.x264-GUACAMOLE");
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
torrentInfo.DownloadUrl.Should().Be("https://filelist.io/download.php?id=665873&passkey=somepass");
torrentInfo.InfoUrl.Should().Be("https://filelist.io/details.php?id=665873");
torrentInfo.DownloadUrl.Should().Be("https://filelist.ro/download.php?id=665873&passkey=somepass");
torrentInfo.InfoUrl.Should().Be("https://filelist.ro/details.php?id=665873");
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2020-01-25 22:20:19"));

View File

@@ -15,7 +15,6 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.MediaCoverTests
{
[TestFixture]
[Ignore("temp - revert")]
public class MediaCoverServiceFixture : CoreTest<MediaCoverService>
{
private Movie _movie;

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