mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-10 15:10:57 -04:00
Compare commits
1 Commits
speed-up-l
...
aphrodite
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72062704ff |
@@ -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
|
||||
|
||||
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -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. -->
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -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
5
.github/stale.yml
vendored
@@ -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
2
.github/support.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
15
README.md
15
README.md
@@ -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 | [](https://github.com/Radarr/Radarr/releases) | [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=windows&runtime=netcore&arch=x64) <br> [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=x64) <br> [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=arm64) [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=arm) <br> [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=osx&runtime=netcore&arch=x64)
|
||||
| Docker - lsio | [](https://hub.docker.com/r/linuxserver/radarr) | [](https://hub.docker.com/r/linuxserver/radarr) |
|
||||
| Docker - hotio | [](https://hub.docker.com/r/hotio/radarr) | [](https://hub.docker.com/r/hotio/radarr) |
|
||||
| Release Type | Branch: develop (stable) | Branch: nightly (semi-unstable) | Branch: aphrodite (very-unstable) |
|
||||
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Binary Releases | [](https://github.com/Radarr/Radarr/releases) | [](https://ci.appveyor.com/project/galli-leo/radarr-usby1/branch/develop/artifacts) | |
|
||||
| Docker - lsio | [](https://hub.docker.com/r/linuxserver/radarr) | [](https://hub.docker.com/r/linuxserver/radarr) | [](https://hub.docker.com/r/linuxserver/radarr) |
|
||||
| Docker - hotio | [](https://hub.docker.com/r/hotio/radarr) | [](https://hub.docker.com/r/hotio/radarr) | [](https://hub.docker.com/r/hotio/radarr) |
|
||||
|
||||
## Support
|
||||
|
||||
[](https://discord.gg/r5wJPt9)
|
||||
[](https://discord.gg/AD3UP37)
|
||||
[](https://www.reddit.com/r/radarr)
|
||||
[](https://github.com/Radarr/Radarr/issues)
|
||||
[](https://github.com/Radarr/Radarr/wiki)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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'
|
||||
]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -170,7 +170,6 @@ class AddNewMovieSearchResult extends Component {
|
||||
imdbId={imdbId}
|
||||
/>
|
||||
}
|
||||
canFlip={true}
|
||||
kind={kinds.INVERSE}
|
||||
position={tooltipPositions.BOTTOM}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -92,5 +92,6 @@
|
||||
}
|
||||
|
||||
.dateIcon {
|
||||
width: 25px;
|
||||
display: inline;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.movieFolder {
|
||||
@add-mixin truncate;
|
||||
|
||||
flex: 0 0 auto;
|
||||
color: $disabledColor;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'
|
||||
]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
} */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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'
|
||||
]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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' },
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
62
macOS/Radarr
Normal 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"
|
||||
83
package.json
83
package.json
@@ -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": [
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>()))
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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[] { };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 |
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>()))
|
||||
|
||||
@@ -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>
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user