1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-13 15:34:56 -04:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Mark McDowall
27316b4239 New: Parse anime version with a space before 'v'
(cherry picked from commit e9123982f33ab35ca022f91f345da05fef23d6dc)
2022-09-25 07:26:51 +00:00
115 changed files with 464 additions and 1147 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,6 +1,6 @@
# These are supported funding model platforms
github: radarr
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: radarr
ko_fi: # Replace with a single Ko-fi username

View File

@@ -45,11 +45,11 @@ body:
- **Database**: Sqlite 3.36.0
value: |
- OS:
- Radarr:
- Docker Install:
- Using Reverse Proxy:
- Browser:
- Database:
- Radarr:
- Docker Install:
- Using Reverse Proxy:
- Browser:
- Database:
render: markdown
validations:
required: true

View File

@@ -5,9 +5,9 @@ body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the feature you are requesting. If a request exists and is closed note that it may only be fixed in an unstable branch.
description: Please search to see if an issue already exists for the feature you are requesting.
options:
- label: I have searched the existing open and closed issues
- label: I have searched the existing issues
required: true
- type: textarea
attributes:

View File

@@ -7,12 +7,8 @@ on:
concurrency: azuresync-${{ github.event.issue.number }}
permissions: {}
jobs:
alert:
permissions:
issues: write # to update issue body
runs-on: ubuntu-latest
steps:
- uses: danhellem/github-actions-issue-to-work-item@master

View File

@@ -5,13 +5,8 @@ on:
schedule:
- cron: '0 0 * * *'
permissions: {}
jobs:
lock:
permissions:
issues: write # to lock issues (dessant/lock-threads)
pull-requests: write # to lock PRs (dessant/lock-threads)
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2

View File

@@ -4,12 +4,8 @@ on:
issues:
types: [labeled, unlabeled, reopened]
permissions: {}
jobs:
support:
permissions:
issues: write # to modify issues
runs-on: ubuntu-latest
steps:
- uses: dessant/support-requests@v2

View File

@@ -1,132 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
<development@radarr.video>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

View File

@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '4.3.1'
majorVersion: '4.2.4'
minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
@@ -761,7 +761,7 @@ stages:
inputs:
buildType: 'current'
artifactName: Packages
itemPattern: '**/$(pattern)'
itemPattern: '/$(pattern)'
targetPath: $(Build.ArtifactStagingDirectory)
- bash: |
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
@@ -1143,5 +1143,4 @@ stages:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
DISCORDCHANNELID: $(discordChannelId)
DISCORDWEBHOOKKEY: $(discordWebhookKey)
DISCORDTHREADID: $(discordThreadId)

View File

@@ -10,12 +10,6 @@
width: 80px;
}
.customFormatScore {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 55px;
}
.releaseGroup {
composes: cell from '~Components/Table/Cells/TableRowCell.css';

View File

@@ -9,7 +9,6 @@ import MovieFormats from 'Movie/MovieFormats';
import MovieLanguage from 'Movie/MovieLanguage';
import MovieQuality from 'Movie/MovieQuality';
import MovieTitleLink from 'Movie/MovieTitleLink';
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
import HistoryDetailsModal from './Details/HistoryDetailsModal';
import HistoryEventTypeCell from './HistoryEventTypeCell';
import styles from './HistoryRow.css';
@@ -169,17 +168,6 @@ class HistoryRow extends Component {
);
}
if (name === 'customFormatScore') {
return (
<TableRowCell
key={name}
className={styles.customFormatScore}
>
{formatCustomFormatScore(data.customFormatScore)}
</TableRowCell>
);
}
if (name === 'releaseGroup') {
return (
<TableRowCell

View File

@@ -192,7 +192,7 @@ class TableOptionsModal extends Component {
<TableOptionsColumnDragSource
key={name}
name={name}
label={columnLabel || label}
label={label || columnLabel}
isVisible={isVisible}
isModifiable={true}
index={index}
@@ -210,7 +210,7 @@ class TableOptionsModal extends Component {
<TableOptionsColumn
key={name}
name={name}
label={columnLabel || label}
label={label || columnLabel}
isVisible={isVisible}
index={index}
isModifiable={false}

View File

@@ -22,7 +22,6 @@ import {
import {
faArrowCircleLeft as fasArrowCircleLeft,
faArrowCircleRight as fasArrowCircleRight,
faAsterisk as fasAsterisk,
faBackward as fasBackward,
faBan as fasBan,
faBars as fasBars,
@@ -155,7 +154,6 @@ export const FILE = farFile;
export const FILM = fasFilm;
export const FILTER = fasFilter;
export const FLAG = fasFlag;
export const FOOTNOTE = fasAsterisk;
export const FOLDER = farFolder;
export const FOLDER_OPEN = fasFolderOpen;
export const GENRE = fasTheaterMasks;

View File

@@ -1,7 +0,0 @@
.modalBody {
composes: modalBody from '~Components/Modal/ModalBody.css';
display: flex;
flex: 1 1 auto;
flex-direction: column;
}

View File

@@ -9,9 +9,8 @@ import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, scrollDirections } from 'Helpers/Props';
import { inputTypes, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './SelectReleaseGroupModalContent.css';
class SelectReleaseGroupModalContent extends Component {
@@ -59,10 +58,7 @@ class SelectReleaseGroupModalContent extends Component {
{translate('ManualImportSetReleaseGroup')}
</ModalHeader>
<ModalBody
className={styles.modalBody}
scrollDirection={scrollDirections.NONE}
>
<ModalBody>
<Form>
<FormGroup>
<FormLabel>{translate('ReleaseGroup')}</FormLabel>
@@ -71,7 +67,6 @@ class SelectReleaseGroupModalContent extends Component {
type={inputTypes.TEXT}
name="releaseGroup"
value={releaseGroup}
autoFocus={true}
onChange={this.onReleaseGroupChange}
/>
</FormGroup>

View File

@@ -11,7 +11,6 @@ import { icons, kinds } from 'Helpers/Props';
import MovieFormats from 'Movie/MovieFormats';
import MovieLanguage from 'Movie/MovieLanguage';
import MovieQuality from 'Movie/MovieQuality';
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
import translate from 'Utilities/String/translate';
import styles from './MovieHistoryRow.css';
@@ -105,10 +104,6 @@ class MovieHistoryRow extends Component {
/>
</TableRowCell>
<TableRowCell key={name}>
{formatCustomFormatScore(data.customFormatScore)}
</TableRowCell>
<RelativeDateCellConnector
date={date}
/>

View File

@@ -1,6 +1,5 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Icon from 'Components/Icon';
import IconButton from 'Components/Link/IconButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import Table from 'Components/Table/Table';
@@ -36,15 +35,6 @@ const columns = [
isSortable: false,
isVisible: true
},
{
name: 'customFormatScore',
label: React.createElement(Icon, {
name: icons.SCORE,
title: 'Custom format score'
}),
isSortable: true,
isVisible: true
},
{
name: 'date',
label: translate('Date'),

View File

@@ -36,9 +36,3 @@
margin: 0;
border: none;
}
.label {
composes: label from '~Components/Label.css';
max-width: 100%;
}

View File

@@ -1,6 +1,5 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import MiddleTruncate from 'react-middle-truncate';
import Card from 'Components/Card';
import Label from 'Components/Label';
import IconButton from 'Components/Link/IconButton';
@@ -125,15 +124,10 @@ class CustomFormat extends Component {
return (
<Label
className={styles.label}
key={index}
kind={kind}
>
<MiddleTruncate
text={item.name}
start={10}
end={14}
/>
{item.name}
</Label>
);
})

View File

@@ -16,20 +16,3 @@
margin-left: 10px;
width: 200px;
}
.footNote {
display: flex;
color: $helpTextColor;
.icon {
margin-top: 3px;
margin-right: 5px;
padding: 2px;
}
code {
padding: 0 1px;
border: 1px solid $borderColor;
background-color: #f7f7f7;
}
}

View File

@@ -3,94 +3,17 @@ import React, { Component } from 'react';
import FieldSet from 'Components/FieldSet';
import SelectInput from 'Components/Form/SelectInput';
import TextInput from 'Components/Form/TextInput';
import Icon from 'Components/Icon';
import Button from 'Components/Link/Button';
import Modal from 'Components/Modal/Modal';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { icons, sizes } from 'Helpers/Props';
import { sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import NamingOption from './NamingOption';
import styles from './NamingModal.css';
const separatorOptions = [
{ key: ' ', value: 'Space ( )' },
{ key: '.', value: 'Period (.)' },
{ key: '_', value: 'Underscore (_)' },
{ key: '-', value: 'Dash (-)' }
];
const caseOptions = [
{ key: 'title', value: translate('DefaultCase') },
{ key: 'lower', value: translate('LowerCase') },
{ key: 'upper', value: translate('UpperCase') }
];
const fileNameTokens = [
{
token: '{Movie Title} - {Quality Full}',
example: 'Movie Title (2010) - HDTV-720p Proper'
}
];
const movieTokens = [
{ token: '{Movie Title}', example: 'Movie\'s Title' },
{ token: '{Movie Title:DE}', example: 'Titel des Films' },
{ token: '{Movie CleanTitle}', example: 'Movies Title' },
{ token: '{Movie TitleThe}', example: 'Movie\'s Title, The' },
{ token: '{Movie OriginalTitle}', example: 'Τίτλος ταινίας' },
{ token: '{Movie CleanOriginalTitle}', example: 'Τίτλος ταινίας' },
{ token: '{Movie TitleFirstCharacter}', example: 'M' },
{ token: '{Movie Collection}', example: 'The Movie Collection' },
{ token: '{Movie Certification}', example: 'R' },
{ token: '{Release Year}', example: '2009' }
];
const movieIdTokens = [
{ token: '{ImdbId}', example: 'tt12345' },
{ token: '{TmdbId}', example: '123456' }
];
const qualityTokens = [
{ token: '{Quality Full}', example: 'HDTV-720p Proper' },
{ token: '{Quality Title}', example: 'HDTV-720p' }
];
const mediaInfoTokens = [
{ token: '{MediaInfo Simple}', example: 'x264 DTS' },
{ token: '{MediaInfo Full}', example: 'x264 DTS [EN+DE]', footNote: 1 },
{ token: '{MediaInfo AudioCodec}', example: 'DTS' },
{ token: '{MediaInfo AudioChannels}', example: '5.1' },
{ token: '{MediaInfo AudioLanguages}', example: '[EN+DE]', footNote: 1 },
{ token: '{MediaInfo SubtitleLanguages}', example: '[DE]', footNote: 1 },
{ token: '{MediaInfo VideoCodec}', example: 'x264' },
{ token: '{MediaInfo VideoBitDepth}', example: '10' },
{ token: '{MediaInfo VideoDynamicRange}', example: 'HDR' },
{ token: '{MediaInfo VideoDynamicRangeType}', example: 'DV HDR10' },
{ token: '{MediaInfo 3D}', example: '3D' }
];
const releaseGroupTokens = [
{ token: '{Release Group}', example: 'Rls Grp' }
];
const editionTokens = [
{ token: '{Edition Tags}', example: 'IMAX' }
];
const customFormatTokens = [
{ token: '{Custom Formats}', example: 'Surround Sound x264' }
];
const originalTokens = [
{ token: '{Original Title}', example: 'Movie.Title.HDTV.x264-EVOLVE' },
{ token: '{Original Filename}', example: 'movie title hdtv.x264-Evolve' }
];
class NamingModal extends Component {
//
@@ -171,6 +94,81 @@ class NamingModal extends Component {
case: tokenCase
} = this.state;
const separatorOptions = [
{ key: ' ', value: 'Space ( )' },
{ key: '.', value: 'Period (.)' },
{ key: '_', value: 'Underscore (_)' },
{ key: '-', value: 'Dash (-)' }
];
const caseOptions = [
{ key: 'title', value: translate('DefaultCase') },
{ key: 'lower', value: translate('LowerCase') },
{ key: 'upper', value: translate('UpperCase') }
];
const fileNameTokens = [
{
token: '{Movie Title} - {Quality Full}',
example: 'Movie Title (2010) - HDTV-720p Proper'
}
];
const movieTokens = [
{ token: '{Movie Title}', example: 'Movie\'s Title' },
{ token: '{Movie Title:DE}', example: 'Titel des Films' },
{ token: '{Movie CleanTitle}', example: 'Movies Title' },
{ token: '{Movie TitleThe}', example: 'Movie\'s Title, The' },
{ token: '{Movie OriginalTitle}', example: 'Τίτλος ταινίας' },
{ token: '{Movie CleanOriginalTitle}', example: 'Τίτλος ταινίας' },
{ token: '{Movie TitleFirstCharacter}', example: 'M' },
{ token: '{Movie Collection}', example: 'The Movie Collection' },
{ token: '{Movie Certification}', example: 'R' },
{ token: '{Release Year}', example: '2009' }
];
const movieIdTokens = [
{ token: '{ImdbId}', example: 'tt12345' },
{ token: '{TmdbId}', example: '123456' }
];
const qualityTokens = [
{ token: '{Quality Full}', example: 'HDTV-720p Proper' },
{ token: '{Quality Title}', example: 'HDTV-720p' }
];
const mediaInfoTokens = [
{ token: '{MediaInfo Simple}', example: 'x264 DTS' },
{ token: '{MediaInfo Full}', example: 'x264 DTS [EN+DE]' },
{ token: '{MediaInfo AudioCodec}', example: 'DTS' },
{ token: '{MediaInfo AudioChannels}', example: '5.1' },
{ token: '{MediaInfo AudioLanguages}', example: '[EN+DE]' },
{ token: '{MediaInfo SubtitleLanguages}', example: '[DE]' },
{ token: '{MediaInfo VideoCodec}', example: 'x264' },
{ token: '{MediaInfo VideoBitDepth}', example: '10' },
{ token: '{MediaInfo VideoDynamicRange}', example: 'HDR' },
{ token: '{MediaInfo VideoDynamicRangeType}', example: 'DV HDR10' }
];
const releaseGroupTokens = [
{ token: '{Release Group}', example: 'Rls Grp' }
];
const editionTokens = [
{ token: '{Edition Tags}', example: 'IMAX' }
];
const customFormatTokens = [
{ token: '{Custom Formats}', example: 'Surround Sound x264' }
];
const originalTokens = [
{ token: '{Original Title}', example: 'Movie.Title.HDTV.x264-EVOLVE' },
{ token: '{Original Filename}', example: 'movie title hdtv.x264-Evolve' }
];
return (
<Modal
isOpen={isOpen}
@@ -299,7 +297,7 @@ class NamingModal extends Component {
<FieldSet legend={translate('MediaInfo')}>
<div className={styles.groups}>
{
mediaInfoTokens.map(({ token, example, footNote }) => {
mediaInfoTokens.map(({ token, example }) => {
return (
<NamingOption
key={token}
@@ -307,7 +305,6 @@ class NamingModal extends Component {
value={value}
token={token}
example={example}
footNote={footNote}
tokenSeparator={tokenSeparator}
tokenCase={tokenCase}
onPress={this.onOptionPress}
@@ -317,14 +314,6 @@ class NamingModal extends Component {
)
}
</div>
<div className={styles.footNote}>
<Icon className={styles.icon} name={icons.FOOTNOTE} />
<div>
MediaInfo Full/AudioLanguages/SubtitleLanguages support a <code>:EN+DE</code> suffix allowing you to filter the languages included in the filename. Use <code>-DE</code> to exclude specific languages.
Appending <code>+</code> (eg <code>:EN+</code>) will output <code>[EN]</code>/<code>[EN+--]</code>/<code>[--]</code> depending on excluded languages. For example <code>{'{'}MediaInfo Full:EN+DE{'}'}</code>.
</div>
</div>
</FieldSet>
<FieldSet legend={translate('ReleaseGroup')}>

View File

@@ -35,15 +35,9 @@
display: flex;
align-items: center;
align-self: stretch;
justify-content: space-between;
flex: 0 0 50%;
padding: 6px 16px;
background-color: #ddd;
.footNote {
padding: 2px;
color: #aaa;
}
}
.lower {

View File

@@ -1,9 +1,8 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import { icons, sizes } from 'Helpers/Props';
import { sizes } from 'Helpers/Props';
import styles from './NamingOption.css';
class NamingOption extends Component {
@@ -40,7 +39,6 @@ class NamingOption extends Component {
token,
tokenSeparator,
example,
footNote,
tokenCase,
isFullFilename,
size
@@ -62,11 +60,6 @@ class NamingOption extends Component {
<div className={styles.example}>
{example.replace(/ /g, tokenSeparator)}
{
footNote !== 0 &&
<Icon className={styles.footNote} name={icons.FOOTNOTE} />
}
</div>
</Link>
);
@@ -76,7 +69,6 @@ class NamingOption extends Component {
NamingOption.propTypes = {
token: PropTypes.string.isRequired,
example: PropTypes.string.isRequired,
footNote: PropTypes.number.isRequired,
tokenSeparator: PropTypes.string.isRequired,
tokenCase: PropTypes.string.isRequired,
isFullFilename: PropTypes.bool.isRequired,
@@ -85,7 +77,6 @@ NamingOption.propTypes = {
};
NamingOption.defaultProps = {
footNote: 0,
size: sizes.SMALL,
isFullFilename: false
};

View File

@@ -85,7 +85,7 @@ class DelayProfile extends Component {
connectDragSource
} = this.props;
let preferred = `Prefer ${titleCase(preferredProtocol)}`;
let preferred = titleCase(preferredProtocol);
if (!enableUsenet) {
preferred = translate('OnlyTorrent');

View File

@@ -83,7 +83,7 @@ class DelayProfiles extends Component {
<div>
<div className={styles.delayProfilesHeader}>
<div className={styles.column}>
{translate('PreferredProtocol')}
{translate('Protocol')}
</div>
<div className={styles.column}>
{translate('UsenetDelay')}

View File

@@ -17,13 +17,6 @@ import { boolSettingShape, numberSettingShape, tagSettingShape } from 'Helpers/P
import translate from 'Utilities/String/translate';
import styles from './EditDelayProfileModalContent.css';
const protocolOptions = [
{ key: 'preferUsenet', value: translate('PreferUsenet') },
{ key: 'preferTorrent', value: translate('PreferTorrent') },
{ key: 'onlyUsenet', value: translate('OnlyUsenet') },
{ key: 'onlyTorrent', value: translate('OnlyTorrent') }
];
function EditDelayProfileModalContent(props) {
const {
id,
@@ -33,6 +26,7 @@ function EditDelayProfileModalContent(props) {
saveError,
item,
protocol,
protocolOptions,
onInputChange,
onProtocolChange,
onSavePress,
@@ -58,24 +52,22 @@ function EditDelayProfileModalContent(props) {
<ModalBody>
{
isFetching ?
<LoadingIndicator /> :
null
isFetching &&
<LoadingIndicator />
}
{
!isFetching && !!error ?
!isFetching && !!error &&
<div>
{translate('UnableToAddANewQualityProfilePleaseTryAgain')}
</div> :
null
</div>
}
{
!isFetching && !error ?
!isFetching && !error &&
<Form {...otherProps}>
<FormGroup>
<FormLabel>{translate('PreferredProtocol')}</FormLabel>
<FormLabel>{translate('Protocol')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
@@ -151,21 +143,19 @@ function EditDelayProfileModalContent(props) {
/>
</FormGroup>
}
</Form> :
null
</Form>
}
</ModalBody>
<ModalFooter>
{
id && id > 1 ?
id && id > 1 &&
<Button
className={styles.deleteButton}
kind={kinds.DANGER}
onPress={onDeleteDelayProfilePress}
>
{translate('Delete')}
</Button> :
null
</Button>
}
<Button
@@ -203,6 +193,7 @@ EditDelayProfileModalContent.propTypes = {
saveError: PropTypes.object,
item: PropTypes.shape(delayProfileShape).isRequired,
protocol: PropTypes.string.isRequired,
protocolOptions: PropTypes.arrayOf(PropTypes.object).isRequired,
onInputChange: PropTypes.func.isRequired,
onProtocolChange: PropTypes.func.isRequired,
onSavePress: PropTypes.func.isRequired,

View File

@@ -5,6 +5,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { saveDelayProfile, setDelayProfileValue } from 'Store/Actions/settingsActions';
import selectSettings from 'Store/Selectors/selectSettings';
import translate from 'Utilities/String/translate';
import EditDelayProfileModalContent from './EditDelayProfileModalContent';
const newDelayProfile = {
@@ -16,6 +17,13 @@ const newDelayProfile = {
tags: []
};
const protocolOptions = [
{ key: 'preferUsenet', value: translate('PreferUsenet') },
{ key: 'preferTorrent', value: translate('PreferTorrent') },
{ key: 'onlyUsenet', value: translate('OnlyUsenet') },
{ key: 'onlyTorrent', value: translate('OnlyTorrent') }
];
function createDelayProfileSelector() {
return createSelector(
(state, { id }) => id,
@@ -71,6 +79,7 @@ function createMapStateToProps() {
return {
protocol,
protocolOptions,
...delayProfile
};
}

View File

@@ -1,7 +1,5 @@
import React from 'react';
import { createAction } from 'redux-actions';
import Icon from 'Components/Icon';
import { filterTypes, icons, sortDirections } from 'Helpers/Props';
import { filterTypes, sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import serverSideCollectionHandlers from 'Utilities/serverSideCollectionHandlers';
@@ -86,15 +84,6 @@ export const defaultState = {
label: translate('SourceTitle'),
isVisible: false
},
{
name: 'customFormatScore',
columnLabel: translate('CustomFormatScore'),
label: React.createElement(Icon, {
name: icons.SCORE,
title: 'Custom format score'
}),
isVisible: false
},
{
name: 'details',
columnLabel: translate('Details'),

View File

@@ -4,7 +4,6 @@ import { batchActions } from 'redux-batched-actions';
import { sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import naturalExpansion from 'Utilities/String/naturalExpansion';
import { set, update, updateItem } from './baseActions';
import createHandleActions from './Creators/createHandleActions';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
@@ -36,7 +35,7 @@ export const defaultState = {
relativePath: function(item, direction) {
const relativePath = item.relativePath;
return naturalExpansion(relativePath.toLowerCase());
return relativePath.toLowerCase();
},
movie: function(item, direction) {

View File

@@ -1,16 +0,0 @@
function formatCustomFormatScore(input) {
const score = Number(input);
if (score > 0) {
return `+${score}`;
}
if (score < 0) {
return score;
}
return '';
}
export default formatCustomFormatScore;

View File

@@ -1,11 +0,0 @@
const regex = /\d+/g;
function naturalExpansion(input) {
if (!input) {
return '';
}
return input.replace(regex, (n) => n.padStart(8, '0'));
}
export default naturalExpansion;

View File

@@ -1,11 +1,9 @@
const regex = /\b\w+/g;
function titleCase(input) {
if (!input) {
return '';
}
return input.replace(regex, (match) => {
return input.replace(/\b\w+/g, (match) => {
return match.charAt(0).toUpperCase() + match.substr(1).toLowerCase();
});
}

View File

@@ -45,7 +45,7 @@
"jquery": "3.6.0",
"lodash": "4.17.21",
"mobile-detect": "1.4.5",
"moment": "2.29.4",
"moment": "2.29.2",
"mousetrap": "1.6.5",
"normalize.css": "8.0.1",
"prop-types": "15.7.2",

View File

@@ -212,7 +212,6 @@ namespace NzbDrone.Common.Test.Http
}
[Test]
[Platform(Exclude = "MacOsX", Reason = "Azure agent update prevents brotli on OSX")]
public void should_execute_get_using_brotli()
{
var request = new HttpRequest($"https://{_httpBinHost}/brotli");

View File

@@ -80,10 +80,6 @@ namespace NzbDrone.Common.Test.InstrumentationTests
// Notifiarr
[TestCase(@"https://xxx.yyy/api/v1/notification/radarr/9pr04sg6-0123-3210-imav-eql2tyu8xyui")]
// Discord
[TestCase(@"https://discord.com/api/webhooks/mySecret")]
[TestCase(@"https://discord.com/api/webhooks/mySecret/01233210")]
public void should_clean_message(string message)
{
var cleansedMessage = CleanseLogMessage.Cleanse(message);

View File

@@ -278,7 +278,7 @@ namespace NzbDrone.Common.Test
[Test]
public void GetUpdateClientExePath()
{
GetIAppDirectoryInfo().GetUpdateClientExePath().Should().BeEquivalentTo(@"C:\Temp\radarr_update\Radarr.Update".AsOsAgnostic().ProcessNameToExe());
GetIAppDirectoryInfo().GetUpdateClientExePath(PlatformType.DotNet).Should().BeEquivalentTo(@"C:\Temp\radarr_update\Radarr.Update.exe".AsOsAgnostic());
}
[Test]

View File

@@ -264,11 +264,6 @@ namespace NzbDrone.Common.Disk
protected virtual void MoveFileInternal(string source, string destination)
{
if (File.Exists(destination))
{
throw new FileAlreadyExistsException("File already exists", destination);
}
File.Move(source, destination);
}

View File

@@ -1,15 +0,0 @@
using System;
namespace NzbDrone.Common.Disk
{
public class FileAlreadyExistsException : Exception
{
public string Filename { get; set; }
public FileAlreadyExistsException(string message, string filename)
: base(message)
{
Filename = filename;
}
}
}

View File

@@ -2,6 +2,13 @@ using System;
namespace NzbDrone.Common.EnvironmentInfo
{
public enum PlatformType
{
DotNet = 0,
Mono = 1,
NetCore = 2
}
public interface IPlatformInfo
{
Version Version { get; }
@@ -9,18 +16,31 @@ namespace NzbDrone.Common.EnvironmentInfo
public class PlatformInfo : IPlatformInfo
{
private static PlatformType _platform;
private static Version _version;
static PlatformInfo()
{
_platform = PlatformType.NetCore;
_version = Environment.Version;
}
public static PlatformType Platform => _platform;
public static bool IsDotNet => Platform == PlatformType.DotNet;
public static bool IsNetCore => Platform == PlatformType.NetCore;
public static string PlatformName
{
get
{
return ".NET";
if (IsDotNet)
{
return ".NET";
}
else
{
return ".NET Core";
}
}
}

View File

@@ -267,9 +267,9 @@ namespace NzbDrone.Common.Extensions
return substring.Substring(0, lastSeparatorIndex);
}
public static string ProcessNameToExe(this string processName)
public static string ProcessNameToExe(this string processName, PlatformType runtime)
{
if (OsInfo.IsWindows)
if (OsInfo.IsWindows || runtime != PlatformType.NetCore)
{
processName += ".exe";
}
@@ -277,6 +277,11 @@ namespace NzbDrone.Common.Extensions
return processName;
}
public static string ProcessNameToExe(this string processName)
{
return processName.ProcessNameToExe(PlatformInfo.Platform);
}
public static string GetAppDataPath(this IAppFolderInfo appFolderInfo)
{
return appFolderInfo.AppDataFolder;
@@ -347,9 +352,9 @@ namespace NzbDrone.Common.Extensions
return Path.Combine(GetUpdatePackageFolder(appFolderInfo), UPDATE_CLIENT_FOLDER_NAME);
}
public static string GetUpdateClientExePath(this IAppFolderInfo appFolderInfo)
public static string GetUpdateClientExePath(this IAppFolderInfo appFolderInfo, PlatformType runtime)
{
return Path.Combine(GetUpdateSandboxFolder(appFolderInfo), UPDATE_CLIENT_EXE_NAME).ProcessNameToExe();
return Path.Combine(GetUpdateSandboxFolder(appFolderInfo), UPDATE_CLIENT_EXE_NAME).ProcessNameToExe(runtime);
}
public static string GetDatabase(this IAppFolderInfo appFolderInfo)

View File

@@ -50,10 +50,7 @@ namespace NzbDrone.Common.Instrumentation
new Regex(@"(?<=\?|&)(X-Plex-Client-Identifier|X-Plex-Token)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Notifiarr
new Regex(@"api/v[0-9]/notification/radarr/(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Discord
new Regex(@"discord.com/api/webhooks/((?<secret>[\w-]+)/)?(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase)
new Regex(@"api/v[0-9]/notification/radarr/(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase)
};
private static readonly Regex CleanseRemoteIPRegex = new Regex(@"(?:Auth-\w+(?<!Failure|Unauthorized) ip|from) (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Compiled);

View File

@@ -70,7 +70,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
[Test]
public void should_return_default_if_no_info_is_known()
{
var result = Subject.Aggregate(_localMovie, null);
var result = Subject.Aggregate(_localMovie, null, false);
result.Languages.Should().Contain(_movie.MovieMetadata.Value.OriginalLanguage);
}
@@ -83,7 +83,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
null,
null);
Subject.Aggregate(_localMovie, null).Languages.Should().Equal(new List<Language> { Language.French });
Subject.Aggregate(_localMovie, null, false).Languages.Should().Equal(new List<Language> { Language.French });
}
[Test]
@@ -94,7 +94,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
null,
null);
var aggregation = Subject.Aggregate(_localMovie, null);
var aggregation = Subject.Aggregate(_localMovie, null, false);
aggregation.Languages.Should().Equal(new List<Language> { Language.German });
}
@@ -107,7 +107,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
new List<Language> { Language.Spanish },
null);
Subject.Aggregate(_localMovie, null).Languages.Should().Equal(new List<Language> { Language.Spanish });
Subject.Aggregate(_localMovie, null, false).Languages.Should().Equal(new List<Language> { Language.Spanish });
}
[Test]
@@ -118,7 +118,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
new List<Language> { Language.Unknown },
null);
Subject.Aggregate(_localMovie, null).Languages.Should().Equal(new List<Language> { Language.French, Language.German });
Subject.Aggregate(_localMovie, null, false).Languages.Should().Equal(new List<Language> { Language.French, Language.German });
}
[Test]
@@ -129,7 +129,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
new List<Language> { Language.Unknown },
new List<Language> { Language.Japanese, Language.English });
Subject.Aggregate(_localMovie, null).Languages.Should().Equal(new List<Language> { Language.Japanese, Language.English });
Subject.Aggregate(_localMovie, null, false).Languages.Should().Equal(new List<Language> { Language.Japanese, Language.English });
}
[Test]
@@ -140,7 +140,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
new List<Language> { Language.Unknown },
new List<Language> { Language.Unknown });
Subject.Aggregate(_localMovie, null).Languages.Should().Equal(new List<Language> { Language.French, Language.German });
Subject.Aggregate(_localMovie, null, false).Languages.Should().Equal(new List<Language> { Language.French, Language.German });
}
}
}

View File

@@ -60,7 +60,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
GivenAugmenters(_fileExtensionAugmenter, nullMock);
var result = Subject.Aggregate(new LocalMovie(), null);
var result = Subject.Aggregate(new LocalMovie(), null, false);
result.Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Extension);
result.Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Extension);
@@ -72,7 +72,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
{
GivenAugmenters(_fileExtensionAugmenter, _nameAugmenter);
var result = Subject.Aggregate(new LocalMovie(), null);
var result = Subject.Aggregate(new LocalMovie(), null, false);
result.Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Name);
result.Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Name);
@@ -84,7 +84,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
{
GivenAugmenters(_fileExtensionAugmenter, _mediaInfoAugmenter);
var result = Subject.Aggregate(new LocalMovie(), null);
var result = Subject.Aggregate(new LocalMovie(), null, false);
result.Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Extension);
result.Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.MediaInfo);
@@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
{
GivenAugmenters(_nameAugmenter, _mediaInfoAugmenter);
var result = Subject.Aggregate(new LocalMovie(), null);
var result = Subject.Aggregate(new LocalMovie(), null, false);
result.Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Name);
result.Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.MediaInfo);
@@ -108,7 +108,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
{
GivenAugmenters(_nameAugmenter, _releaseNameAugmenter);
var result = Subject.Aggregate(new LocalMovie(), new DownloadClientItem());
var result = Subject.Aggregate(new LocalMovie(), new DownloadClientItem(), false);
result.Quality.SourceDetectionSource.Should().Be(QualityDetectionSource.Name);
result.Quality.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Name);
@@ -120,7 +120,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
{
GivenAugmenters(_nameAugmenter, _releaseNameAugmenter);
var result = Subject.Aggregate(new LocalMovie(), new DownloadClientItem());
var result = Subject.Aggregate(new LocalMovie(), new DownloadClientItem(), false);
result.Quality.Revision.Version.Should().Be(1);
result.Quality.RevisionDetectionSource.Should().Be(QualityDetectionSource.Unknown);
@@ -134,7 +134,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
GivenAugmenters(_nameAugmenter, _releaseNameAugmenter);
var result = Subject.Aggregate(new LocalMovie(), new DownloadClientItem());
var result = Subject.Aggregate(new LocalMovie(), new DownloadClientItem(), false);
result.Quality.Revision.Version.Should().Be(2);
result.Quality.RevisionDetectionSource.Should().Be(QualityDetectionSource.Name);
@@ -148,7 +148,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
GivenAugmenters(_nameAugmenter, _releaseNameAugmenter);
var result = Subject.Aggregate(new LocalMovie(), new DownloadClientItem());
var result = Subject.Aggregate(new LocalMovie(), new DownloadClientItem(), false);
result.Quality.Revision.Version.Should().Be(0);
result.Quality.RevisionDetectionSource.Should().Be(QualityDetectionSource.Name);
@@ -165,7 +165,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
GivenAugmenters(_nameAugmenter, _releaseNameAugmenter);
var result = Subject.Aggregate(new LocalMovie(), new DownloadClientItem());
var result = Subject.Aggregate(new LocalMovie(), new DownloadClientItem(), false);
result.Quality.Revision.Version.Should().Be(2);
result.Quality.RevisionDetectionSource.Should().Be(QualityDetectionSource.Name);

View File

@@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
Movie = _movie
};
Subject.Aggregate(localMovie, null);
Subject.Aggregate(localMovie, null, false);
localMovie.ReleaseGroup.Should().Be("Viva");
}
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
Movie = _movie
};
Subject.Aggregate(localMovie, null);
Subject.Aggregate(localMovie, null, false);
localMovie.ReleaseGroup.Should().Be("Drone");
}
@@ -75,7 +75,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
Movie = _movie
};
Subject.Aggregate(localMovie, null);
Subject.Aggregate(localMovie, null, false);
localMovie.ReleaseGroup.Should().Be("Wizzy");
}
@@ -95,7 +95,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
Movie = _movie
};
Subject.Aggregate(localMovie, null);
Subject.Aggregate(localMovie, null, false);
localMovie.ReleaseGroup.Should().Be("FraMeSToR");
}
@@ -115,7 +115,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
Movie = _movie
};
Subject.Aggregate(localMovie, null);
Subject.Aggregate(localMovie, null, false);
localMovie.ReleaseGroup.Should().Be("FraMeSToR");
}

View File

@@ -1,153 +0,0 @@
using System.Collections.Generic;
using System.IO;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MovieImport;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MovieImport
{
[TestFixture]
public class GetSceneNameFixture : CoreTest
{
private LocalMovie _localMovie;
private string _movieName = "movie.title.2022.dvdrip.x264-ingot";
[SetUp]
public void Setup()
{
var movie = Builder<Movie>.CreateNew()
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
.With(s => s.Path = @"C:\Test\Movies\Movie Title".AsOsAgnostic())
.Build();
_localMovie = new LocalMovie
{
Movie = movie,
Path = Path.Combine(movie.Path, "Movie Title - 2022 - Episode Title.mkv"),
Quality = new QualityModel(Quality.Bluray720p),
ReleaseGroup = "DRONE"
};
}
private void GivenExistingFileOnDisk()
{
Mocker.GetMock<IMediaFileService>()
.Setup(s => s.GetFilesWithRelativePath(It.IsAny<int>(), It.IsAny<string>()))
.Returns(new List<MovieFile>());
}
[Test]
public void should_use_download_client_item_title_as_scene_name()
{
_localMovie.DownloadClientMovieInfo = new ParsedMovieInfo
{
ReleaseTitle = _movieName
};
SceneNameCalculator.GetSceneName(_localMovie).Should()
.Be(_movieName);
}
[Test]
public void should_not_use_download_client_item_title_as_scene_name_if_there_are_other_video_files()
{
_localMovie.OtherVideoFiles = true;
_localMovie.DownloadClientMovieInfo = new ParsedMovieInfo
{
ReleaseTitle = _movieName
};
_localMovie.Path = Path.Combine(@"C:\Test\Unsorted Movies", _movieName)
.AsOsAgnostic();
SceneNameCalculator.GetSceneName(_localMovie).Should()
.BeNull();
}
[Test]
public void should_use_file_name_as_scenename_only_if_it_looks_like_scenename()
{
_localMovie.Path = Path.Combine(@"C:\Test\Unsorted Movies", _movieName + ".mkv")
.AsOsAgnostic();
SceneNameCalculator.GetSceneName(_localMovie).Should()
.Be(_movieName);
}
[Test]
public void should_not_use_file_name_as_scenename_if_it_doesnt_look_like_scenename()
{
_localMovie.Path = Path.Combine(@"C:\Test\Unsorted Movies", _movieName, "aaaaa.mkv")
.AsOsAgnostic();
SceneNameCalculator.GetSceneName(_localMovie).Should()
.BeNull();
}
[Test]
public void should_use_folder_name_as_scenename_only_if_it_looks_like_scenename()
{
_localMovie.FolderMovieInfo = new ParsedMovieInfo
{
ReleaseTitle = _movieName
};
SceneNameCalculator.GetSceneName(_localMovie).Should()
.Be(_movieName);
}
[Test]
public void should_not_use_folder_name_as_scenename_if_it_doesnt_look_like_scenename()
{
_localMovie.Path = Path.Combine(@"C:\Test\Unsorted Movies", _movieName, "aaaaa.mkv")
.AsOsAgnostic();
_localMovie.FolderMovieInfo = new ParsedMovieInfo
{
ReleaseTitle = "aaaaa"
};
SceneNameCalculator.GetSceneName(_localMovie).Should()
.BeNull();
}
[Test]
public void should_not_use_folder_name_as_scenename_if_there_are_other_video_files()
{
_localMovie.OtherVideoFiles = true;
_localMovie.Path = Path.Combine(@"C:\Test\Unsorted Movies", _movieName, "aaaaa.mkv")
.AsOsAgnostic();
_localMovie.FolderMovieInfo = new ParsedMovieInfo
{
ReleaseTitle = _movieName
};
SceneNameCalculator.GetSceneName(_localMovie).Should()
.BeNull();
}
[TestCase(".mkv")]
[TestCase(".par2")]
[TestCase(".nzb")]
public void should_remove_extension_from_nzb_title_for_scene_name(string extension)
{
_localMovie.DownloadClientMovieInfo = new ParsedMovieInfo
{
ReleaseTitle = _movieName + extension
};
SceneNameCalculator.GetSceneName(_localMovie).Should()
.Be(_movieName);
}
}
}

View File

@@ -159,6 +159,84 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport
Times.Never());
}
[Test]
public void should_use_nzb_title_as_scene_name()
{
GivenNewDownload();
_downloadClientItem.Title = "malcolm.in.the.middle.2015.dvdrip.xvid-ingot";
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<MovieFile>(c => c.SceneName == _downloadClientItem.Title)));
}
[TestCase(".mkv")]
[TestCase(".par2")]
[TestCase(".nzb")]
public void should_remove_extension_from_nzb_title_for_scene_name(string extension)
{
GivenNewDownload();
var title = "malcolm.in.the.middle.2015.dvdrip.xvid-ingot";
_downloadClientItem.Title = title + extension;
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<MovieFile>(c => c.SceneName == title)));
}
[Test]
public void should_use_file_name_as_scenename_only_if_it_looks_like_scenename()
{
GivenNewDownload();
_approvedDecisions.First().LocalMovie.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "movie.title.2018.dvdrip.xvid-ingot.mkv");
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<MovieFile>(c => c.SceneName == "movie.title.2018.dvdrip.xvid-ingot")));
}
[Test]
public void should_not_use_file_name_as_scenename_if_it_doesnt_looks_like_scenename()
{
GivenNewDownload();
_approvedDecisions.First().LocalMovie.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "aaaaa.mkv");
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<MovieFile>(c => c.SceneName == null)));
}
[Test]
public void should_use_folder_name_as_scenename_only_if_it_looks_like_scenename()
{
GivenNewDownload();
_approvedDecisions.First().LocalMovie.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "aaaaa.mkv");
_approvedDecisions.First().LocalMovie.FolderMovieInfo = new ParsedMovieInfo
{
ReleaseTitle = "movie.title.2018.dvdrip.xvid-ingot"
};
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<MovieFile>(c => c.SceneName == "movie.title.2018.dvdrip.xvid-ingot")));
}
[Test]
public void should_not_use_folder_name_as_scenename_if_it_doesnt_looks_like_scenename()
{
GivenNewDownload();
_approvedDecisions.First().LocalMovie.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "aaaaa.mkv");
_approvedDecisions.First().LocalMovie.FolderMovieInfo = new ParsedMovieInfo
{
ReleaseTitle = "aaaaa.mkv"
};
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<MovieFile>(c => c.SceneName == null)));
}
[Test]
public void should_import_larger_files_first()
{
@@ -333,18 +411,5 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<MovieFile>(c => c.OriginalFilePath == $"subfolder\\{name}.mkv".AsOsAgnostic())));
}
[Test]
public void should_include_scene_name_with_new_downloads()
{
var firstDecision = _approvedDecisions.First();
firstDecision.LocalMovie.SceneName = "Movie.Title.2022.dvdrip-DRONE";
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IUpgradeMediaFiles>()
.Verify(v => v.UpgradeMovieFile(It.Is<MovieFile>(e => e.SceneName == firstDecision.LocalMovie.SceneName), _approvedDecisions.First().LocalMovie, false),
Times.Once());
}
}
}

View File

@@ -102,8 +102,8 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport
private void GivenAugmentationSuccess()
{
Mocker.GetMock<IAggregationService>()
.Setup(s => s.Augment(It.IsAny<LocalMovie>(), It.IsAny<DownloadClientItem>()))
.Callback<LocalMovie, DownloadClientItem>((localMovie, downloadClientItem) =>
.Setup(s => s.Augment(It.IsAny<LocalMovie>(), It.IsAny<DownloadClientItem>(), It.IsAny<bool>()))
.Callback<LocalMovie, DownloadClientItem, bool>((localMovie, downloadClientItem, otherFiles) =>
{
localMovie.Movie = _localMovie.Movie;
});
@@ -173,7 +173,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport
GivenSpecifications(_pass1);
Mocker.GetMock<IAggregationService>()
.Setup(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<DownloadClientItem>()))
.Setup(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<DownloadClientItem>(), It.IsAny<bool>()))
.Throws<TestException>();
_videoFiles = new List<string>
@@ -188,7 +188,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport
Subject.GetImportDecisions(_videoFiles, _movie);
Mocker.GetMock<IAggregationService>()
.Verify(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<DownloadClientItem>()), Times.Exactly(_videoFiles.Count));
.Verify(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<DownloadClientItem>(), It.IsAny<bool>()), Times.Exactly(_videoFiles.Count));
ExceptionVerification.ExpectedErrors(3);
}
@@ -209,7 +209,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport
var fileNames = _videoFiles.Select(System.IO.Path.GetFileName);
Mocker.GetMock<IAggregationService>()
.Setup(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<DownloadClientItem>()))
.Setup(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<DownloadClientItem>(), It.IsAny<bool>()))
.Throws<TestException>();
}
@@ -217,7 +217,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport
public void should_return_a_decision_when_exception_is_caught()
{
Mocker.GetMock<IAggregationService>()
.Setup(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<DownloadClientItem>()))
.Setup(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<DownloadClientItem>(), It.IsAny<bool>()))
.Throws<TestException>();
_videoFiles = new List<string>

View File

@@ -425,6 +425,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
.Should().Be("South.Park.H264.DTS.[EN+ES+IT]");
}
[Ignore("not currently supported")]
[Test]
public void should_format_mediainfo_3d_properly()
{
@@ -668,27 +669,6 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
.Should().Be(expected);
}
[TestCase("eng/deu", "", "[EN+DE]")]
[TestCase("eng/nld/deu", "", "[EN+NL+DE]")]
[TestCase("eng/deu", ":DE", "[DE]")]
[TestCase("eng/nld/deu", ":EN+NL", "[EN+NL]")]
[TestCase("eng/nld/deu", ":NL+EN", "[NL+EN]")]
[TestCase("eng/nld/deu", ":-NL", "[EN+DE]")]
[TestCase("eng/nld/deu", ":DE+", "[DE+-]")]
[TestCase("eng/nld/deu", ":DE+NO.", "[DE].")]
[TestCase("eng/nld/deu", ":-EN-", "[NL+DE]-")]
public void should_format_subtitle_languages_all(string subtitleLanguages, string format, string expected)
{
_movieFile.ReleaseGroup = null;
GivenMediaInfoModel(subtitles: subtitleLanguages);
_namingConfig.StandardMovieFormat = "{MediaInfo SubtitleLanguages" + format + "}End";
Subject.BuildFileName(_movie, _movieFile)
.Should().Be(expected + "End");
}
[TestCase(HdrFormat.None, "South.Park")]
[TestCase(HdrFormat.Hlg10, "South.Park.HDR")]
[TestCase(HdrFormat.Hdr10, "South.Park.HDR")]

View File

@@ -1,6 +1,7 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
@@ -8,9 +9,9 @@ namespace NzbDrone.Core.Test.ParserTests
[TestFixture]
public class AnimeVersionFixture : CoreTest
{
[TestCase("Anime Title - 2018 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 1)]
[TestCase("Anime Title - 2018v2 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 2)]
[TestCase("Anime Title - 2018 v2 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 2)]
[TestCase("Anime Title - S01E01 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 1)]
[TestCase("Anime Title - S01E01v2 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 2)]
[TestCase("Anime Title - S01E01 v2 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 2)]
[TestCase("[SubsPlease] Anime Title - 01 (1080p) [B1F227CF]", 1)]
[TestCase("[SubsPlease] Anime Title - 01v2 (1080p) [B1F227CF]", 2)]
[TestCase("[SubsPlease] Anime Title - 01 v2 (1080p) [B1F227CF]", 2)]

View File

@@ -71,8 +71,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie Title (2020)[BDRemux AVC 1080p][E-AC3 DD Plus 5.1 Castellano-Inglés Subs]")]
[TestCase("Movie Title (2020) [UHDRemux2160p HDR][DTS-HD MA 5.1 AC3 5.1 Castellano - True-HD 7.1 Atmos Inglés Subs]")]
[TestCase("Movie Title (2016) [UHDRemux 2160p SDR] [Castellano DD 5.1 - Inglés DTS-HD MA 5.1 Subs]")]
[TestCase("Movie Title 2022 [HDTV 720p][Cap.101][AC3 5.1 Castellano][www.pctnew.ORG]")]
[TestCase("Movie Title 2022 [HDTV 720p][Cap.206][AC3 5.1 Español Castellano]")]
public void should_parse_language_spanish(string postTitle)
{
var result = Parser.Parser.ParseMovieTitle(postTitle, true);

View File

@@ -37,13 +37,16 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("or")]
[TestCase("an")]
[TestCase("of")]
public void should_remove_common_words_from_middle_of_title(string word)
public void should_remove_common_words(string word)
{
var dirtyFormat = new[]
{
"word.{0}.word",
"word {0} word",
"word-{0}-word"
"word-{0}-word",
"word.word.{0}",
"word-word-{0}",
"word-word {0}",
};
foreach (var s in dirtyFormat)
@@ -53,27 +56,6 @@ namespace NzbDrone.Core.Test.ParserTests
}
}
[TestCase("the")]
[TestCase("and")]
[TestCase("or")]
[TestCase("an")]
[TestCase("of")]
public void should_not_remove_common_words_from_end_of_title(string word)
{
var dirtyFormat = new[]
{
"word.word.{0}",
"word-word-{0}",
"word-word {0}"
};
foreach (var s in dirtyFormat)
{
var dirty = string.Format(s, word);
dirty.CleanMovieTitle().Should().Be("wordword" + word.ToLower());
}
}
[Test]
public void should_remove_a_from_middle_of_title()
{

View File

@@ -7,7 +7,6 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class QualityParserFixture : CoreTest
{
[SetUp]
@@ -198,8 +197,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie Title (2020) MULTi WEB 1080p x264-JiHEFF (S:317/L:28)", false)]
[TestCase("Movie.Titles.2020.1080p.NF.WEB.DD2.0.x264-SNEAkY", false)]
[TestCase("The.Movie.2022.NORDiC.1080p.DV.HDR.WEB.H 265-NiDHUG", false)]
[TestCase("Movie Title 2018 [WEB 1080p HEVC Opus] [Netaro]", false)]
[TestCase("Movie Title 2018 (WEB 1080p HEVC Opus) [Netaro]", false)]
public void should_parse_webdl1080p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R1080p);
@@ -208,7 +205,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie.Name.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", false)]
[TestCase("Movie.Name.1x04.ITA.1080p.WEBMux.x264-NovaRip", false)]
[TestCase("Movie.Name.2019.S02E07.Chapter.15.The.Believer.4Kto1080p.DSNYP.Webrip.x265.10bit.EAC3.5.1.Atmos.GokiTAoE", false)]
[TestCase("Movie.Title.2019.1080p.AMZN.WEB-Rip.DDP.5.1.HEVC", false)]
public void should_parse_webrip1080p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Source.WEBRIP, proper, Resolution.R1080p);
@@ -317,8 +313,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie.Title.M.2008.USA.BluRay.Remux.1080p.MPEG-2.DD.5.1-TDD")]
[TestCase("Movie.Title.2018.1080p.BluRay.REMUX.MPEG-2.DTS-HD.MA.5.1-EPSiLON")]
[TestCase("Movie.Title.II.2003.4K.BluRay.Remux.1080p.AVC.DTS-HD.MA.5.1-BMF")]
[TestCase("Movie Title 2022 (BDRemux 1080p HEVC FLAC) [Netaro]")]
[TestCase("[Vodes] Movie Title - Other Title (2020) [BDRemux 1080p HEVC Dual-Audio]")]
public void should_parse_remux1080p_quality(string title)
{
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R1080p, Modifier.REMUX);

View File

@@ -51,7 +51,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Some.Really.Bad.Movie.Title.[2021].1080p.WEB-HDRip.Dual.Audio.[Hindi.[Clean]. .English].x264.AAC.DD.2.0.By.Full4Movies.mkv-xpost", null)]
[TestCase("The.Movie.Title.2013.1080p.10bit.AMZN.WEB-DL.DDP5.1.HEVC-Vyndros", "Vyndros")]
[TestCase("Movie.Name.2022.1080p.BluRay.x264-[YTS.AG]", "YTS.AG")]
[TestCase("Movie.Title.2019.1080p.AMZN.WEB-Rip.DDP.5.1.HEVC", null)]
public void should_parse_expected_release_group(string title, string expected)
{
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);
@@ -106,7 +105,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie Title (2009) (2160p PMTP WEB-DL Hybrid H265 DV HDR10+ DDP Atmos 5.1 English - HONE)", "HONE")]
[TestCase("Why.Cant.You.Use.Normal.Characters.2021.2160p.UHD.HDR10+.BluRay.TrueHD.Atmos.7.1.x265-ZØNEHD", "ZØNEHD")]
[TestCase("Movie.Should.Not.Use.Dots.2022.1080p.BluRay.x265.10bit.Tigole", "Tigole")]
[TestCase("Movie.Title.2005.2160p.UHD.BluRay.TrueHD 7.1.Atmos.x265 - HQMUX", "HQMUX")]
public void should_parse_exception_release_group(string title, string expected)
{
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);

View File

@@ -69,7 +69,7 @@ namespace NzbDrone.Core.Test.UpdateTests
.Returns(true);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FileExists(It.Is<string>(s => s.EndsWith("Radarr.Update".ProcessNameToExe()))))
.Setup(v => v.FileExists(It.Is<string>(s => s.EndsWith("Radarr.Update.exe"))))
.Returns(true);
_sandboxFolder = Mocker.GetMock<IAppFolderInfo>().Object.GetUpdateSandboxFolder();
@@ -165,7 +165,7 @@ namespace NzbDrone.Core.Test.UpdateTests
public void should_return_with_warning_if_updater_doesnt_exists()
{
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FileExists(It.Is<string>(s => s.EndsWith("Radarr.Update".ProcessNameToExe()))))
.Setup(v => v.FileExists(It.Is<string>(s => s.EndsWith("Radarr.Update.exe"))))
.Returns(false);
Subject.Execute(new ApplicationUpdateCommand());

View File

@@ -1,7 +1,6 @@
using System;
using System.Data.Common;
using System.Data.SQLite;
using System.Net.Sockets;
using NLog;
using Npgsql;
using NzbDrone.Common.Disk;
@@ -125,37 +124,6 @@ namespace NzbDrone.Core.Datastore
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wiki.servarr.com/radarr/faq#i-am-getting-an-error-database-disk-image-is-malformed", e, fileName);
}
catch (NpgsqlException e)
{
if (e.InnerException is SocketException)
{
var retryCount = 3;
while (true)
{
Logger.Error(e, "Failure to connect to Postgres DB, {0} retries remaining", retryCount);
try
{
_migrationController.Migrate(connectionString, migrationContext);
}
catch (Exception ex)
{
if (--retryCount > 0)
{
System.Threading.Thread.Sleep(5000);
continue;
}
throw new RadarrStartupException(ex, "Error creating main database");
}
}
}
else
{
throw new RadarrStartupException(e, "Error creating main database");
}
}
catch (Exception e)
{
throw new RadarrStartupException(e, "Error creating main database");

View File

@@ -70,7 +70,6 @@ namespace NzbDrone.Core.DecisionEngine
{
DownloadDecision decision = null;
_logger.ProgressTrace("Processing release {0}/{1}", reportNumber, reports.Count);
_logger.Debug("Processing release '{0}' from '{1}'", report.Title, report.Indexer);
try
{

View File

@@ -110,7 +110,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
public override string Name => "rTorrent";
public override ProviderMessage Message => new ProviderMessage($"rTorrent will not pause torrents when they meet the seed criteria. Radarr will handle automatic removal of torrents based on the current seed criteria in Settings->Indexers only when Remove Completed is enabled. After importing it will also set \"{_imported_view}\" as an rTorrent view, which can be used in rTorrent scripts to customize behavior.", ProviderMessageType.Info);
public override ProviderMessage Message => new ProviderMessage($"Radarr will handle automatic removal of torrents based on the current seed criteria in Settings->Indexers. After importing it will also set \"{_imported_view}\" as an rTorrent view, which can be used in rTorrent scripts to customize behavior.", ProviderMessageType.Info);
public override IEnumerable<DownloadClientItem> GetItems()
{

View File

@@ -37,10 +37,10 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to rTorrent")]
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to ruTorrent")]
public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "Url Path", Type = FieldType.Textbox, HelpText = "Path to the XMLRPC endpoint, see http(s)://[host]:[port]/[urlPath]. This is usually RPC2 or [path to ruTorrent]/plugins/rpc/rpc.php when using ruTorrent.")]
[FieldDefinition(3, Label = "Url Path", Type = FieldType.Textbox, HelpText = "Path to the XMLRPC endpoint, see http(s)://[host]:[port]/[urlPath]. When using ruTorrent this usually is RPC2 or (path to ruTorrent)/plugins/rpc/rpc.php")]
public string UrlBase { get; set; }
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[FieldDefinition(10, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing movies that were released over 14 days ago")]
public int OlderMoviePriority { get; set; }
[FieldDefinition(11, Label = "Add Stopped", Type = FieldType.Checkbox, HelpText = "Enabling will add torrents and magnets to rTorrent in a stopped state. This may break magnet files.")]
[FieldDefinition(11, Label = "Add Stopped", Type = FieldType.Checkbox, HelpText = "Enabling will add torrents and magnets to ruTorrent in a stopped state. This may break magnet files.")]
public bool AddStopped { get; set; }
public NzbDroneValidationResult Validate()

View File

@@ -109,7 +109,7 @@ namespace NzbDrone.Core.Download
movieGrabbedEvent.DownloadId = downloadClientId;
}
_logger.ProgressInfo("Report sent to {0} from indexer {1}. {2}", downloadClient.Definition.Name, remoteMovie.Release.Indexer, downloadTitle);
_logger.ProgressInfo("Report sent to {0}. {1}", downloadClient.Definition.Name, downloadTitle);
_eventAggregator.PublishEvent(movieGrabbedEvent);
}
}

View File

@@ -52,7 +52,7 @@ namespace NzbDrone.Core.HealthCheck
.AddQueryParam("version", BuildInfo.Version)
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
.AddQueryParam("runtime", "netcore")
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
.AddQueryParam("branch", _configFileProvider.Branch)
.Build();
try

View File

@@ -152,7 +152,6 @@ namespace NzbDrone.Core.History
history.Data.Add("Guid", message.Movie.Release.Guid);
history.Data.Add("TmdbId", message.Movie.Release.TmdbId.ToString());
history.Data.Add("Protocol", ((int)message.Movie.Release.DownloadProtocol).ToString());
history.Data.Add("CustomFormatScore", message.Movie.CustomFormatScore.ToString());
history.Data.Add("IndexerFlags", message.Movie.Release.IndexerFlags.ToString());
history.Data.Add("IndexerId", message.Movie.Release.IndexerId.ToString());

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
@@ -8,7 +7,6 @@ namespace NzbDrone.Core.ImportLists.ImportListMovies
public interface IImportListMovieRepository : IBasicRepository<ImportListMovie>
{
List<ImportListMovie> GetAllForLists(List<int> listIds);
bool ExistsByMetadataId(int metadataId);
}
public class ImportListMovieRepository : BasicRepository<ImportListMovie>, IImportListMovieRepository
@@ -22,12 +20,5 @@ namespace NzbDrone.Core.ImportLists.ImportListMovies
{
return Query(x => listIds.Contains(x.ListId));
}
public bool ExistsByMetadataId(int metadataId)
{
var movies = Query(x => x.MovieMetadataId == metadataId);
return movies.Any();
}
}
}

View File

@@ -15,7 +15,6 @@ namespace NzbDrone.Core.ImportLists.ImportListMovies
List<ImportListMovie> SyncMoviesForList(List<ImportListMovie> listMovies, int listId);
void RemoveListMovie(ImportListMovie listMovie);
ImportListMovie GetById(int id);
bool ExistsByMetadataId(int metadataId);
}
public class ImportListMovieService : IImportListMovieService, IHandleAsync<ProviderDeletedEvent<IImportList>>
@@ -80,10 +79,5 @@ namespace NzbDrone.Core.ImportLists.ImportListMovies
var moviesOnList = _importListMovieRepository.GetAllForLists(new List<int> { message.ProviderId });
_importListMovieRepository.DeleteMany(moviesOnList);
}
public bool ExistsByMetadataId(int metadataId)
{
return _importListMovieRepository.ExistsByMetadataId(metadataId);
}
}
}

View File

@@ -5,7 +5,6 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.ImportLists.Exceptions;
using NzbDrone.Core.ImportLists.ImportListMovies;
using NzbDrone.Core.Notifications.Plex.PlexTv;
using NzbDrone.Core.Notifications.Plex.Server;
namespace NzbDrone.Core.ImportLists.Plex
@@ -20,7 +19,7 @@ namespace NzbDrone.Core.ImportLists.Plex
public virtual IList<ImportListMovie> ParseResponse(ImportListResponse importResponse)
{
List<PlexWatchlistItem> items;
List<PlexSectionItem> items;
_importResponse = importResponse;
@@ -31,7 +30,7 @@ namespace NzbDrone.Core.ImportLists.Plex
return movies;
}
items = Json.Deserialize<PlexResponse<PlexWatchlistRespone>>(_importResponse.Content)
items = Json.Deserialize<PlexResponse<PlexSectionResponse>>(_importResponse.Content)
.MediaContainer
.Items;

View File

@@ -30,7 +30,7 @@
"NoListRecommendations": "No s'han trobat elements de llista ni recomanacions; per començar, podeu afegir una pel·lícula nova, importar-ne algunes existents o afegir una llista.",
"NotAvailable": "No disponible",
"PreferAndUpgrade": "Marca preferit i actualitza",
"QualityProfileInUse": "No es pot suprimir un perfil de qualitat usat a una pel·lícula, llista o col·lecció",
"QualityProfileInUse": "No es pot suprimir un perfil de qualitat usat a una pel·lícula",
"RadarrTags": "Etiquetes de Radarr",
"Reddit": "Reddit",
"RSSSyncInterval": "Interval de sincronització RSS",
@@ -594,7 +594,7 @@
"IndexersSettingsSummary": "Indexadors i restriccions de llançament",
"IndexerStatusCheckAllClientMessage": "Tots els indexadors no estan disponibles a causa d'errors",
"IndexerStatusCheckSingleClientMessage": "Els indexadors no estan disponibles a causa d'errors: {0}",
"IndexerTagHelpText": "Utilitzeu aquest indexador només per a pel·lícules amb almenys una etiqueta coincident. Deixeu-ho en blanc per utilitzar-ho amb totes les pel·lícules.",
"IndexerTagHelpText": "Utilitzeu aquest indexador només per a pel·lícules amb almenys una etiqueta coincident. Deixa en blanc per utilitzar-lo amb totes les pel·lícules.",
"InstallLatest": "Instal·la l'últim",
"LanguageHelpText": "Idioma per a llançaments",
"Large": "Gran",
@@ -804,7 +804,7 @@
"RemotePathMappingCheckFilesLocalWrongOSPath": "El client de baixada local {0} ha informat de fitxers a {1}, però el camí {2} no és vàlid. Reviseu la configuració del vostre client de baixada.",
"RemotePathMappingCheckFilesWrongOSPath": "El client de baixada remota {0} ha informat de fitxers a {1}, però el camí {2} no és vàlid. Reviseu els mapes de camins remots i baixeu la configuració del client.",
"RemotePathMappingCheckFolderPermissions": "Radarr pot veure però no accedir al directori de descàrregues {0}. Error de permisos probable.",
"RemotePathMappingCheckGenericPermissions": "El client de baixada {0} col·loca les baixades a {1}, però Radarr no pot veure aquest directori. És possible que hàgiu d'ajustar els permisos de la carpeta.",
"RemotePathMappingCheckGenericPermissions": "El client de baixada {0} col·loca les baixades a {1}, però Radarr no pot veure aquest directori. És possible que hàgiu d'ajustar els permisos de la carpeta.",
"RemotePathMappingCheckImportFailed": "Radarr no ha pogut importar una pel·lícula. Comproveu els vostres registres per obtenir més informació.",
"RemotePathMappingCheckLocalFolderMissing": "El client de descàrrega remota {0} col·loca les baixades a {1}, però sembla que aquest directori no existeix. És probable que falti o sigui incorrecte l'assignació de camins remots.",
"RemotePathMappingCheckLocalWrongOSPath": "El client de baixada local {0} col·loca les baixades a {1}, però el camí {2} no és vàlid. Reviseu la configuració del vostre client de baixada.",

View File

@@ -339,7 +339,7 @@
"ChangeHasNotBeenSavedYet": "Änderung wurde noch nicht gespeichert",
"CheckForFinishedDownloadsInterval": "Intervall zum prüfen von fertigen Downloads",
"CleanLibraryLevel": "Mediathek aufräumen",
"ClickToChangeLanguage": "Sprache ändern ...",
"ClickToChangeLanguage": "Sprache ändern...",
"ClickToChangeQuality": "Hier klicken um die Qualität zu ändern",
"ClientPriority": "Priorität",
"CloneFormatTag": "Format Tag kopieren",
@@ -889,7 +889,7 @@
"RadarrUpdated": "Radarr wurde aktualisiert",
"RadarrCalendarFeed": "Radarr Kalender Feed",
"QueueIsEmpty": "Warteschlange ist leer",
"QualityProfileInUse": "Ein Qualitätsprofil mit zugeordneten Filmen, Listen oder Sammlungen kann nicht gelöscht werden",
"QualityProfileInUse": "Ein Qualitätsprofil mit zugeordneten Filmen kann nicht gelöscht werden",
"QualityOrLangCutoffHasNotBeenMet": "Qualität oder Sprache haben die Schwelle noch nicht erreicht",
"QualityLimitsHelpText": "Limitierungen werden automatisch an die Filmlänge angepasst.",
"QualitiesHelpText": "Qualitätsprofile oben sind mehr bevorzugt. Profile in einer Gruppe sind gleichgestellt. Nur ausgewählte werden gesucht",

View File

@@ -702,7 +702,6 @@
"PreferIndexerFlags": "Prefer Indexer Flags",
"PreferIndexerFlagsHelpText": "Prioritize releases with special flags",
"Preferred": "Preferred",
"PreferredProtocol": "Preferred Protocol",
"PreferredSize": "Preferred Size",
"PreferTorrent": "Prefer Torrent",
"PreferUsenet": "Prefer Usenet",
@@ -730,7 +729,7 @@
"PtpOldSettingsCheckMessage": "The following PassThePopcorn indexers have deprecated settings and should be updated: {0}",
"PublishedDate": "Published Date",
"Qualities": "Qualities",
"QualitiesHelpText": "Qualities higher in the list are more preferred even if not checked. Qualities within the same group are equal. Only checked qualities are wanted",
"QualitiesHelpText": "Qualities higher in the list are more preferred. Qualities within the same group are equal. Only checked qualities are wanted",
"Quality": "Quality",
"QualityCutoffHasNotBeenMet": "Quality cutoff has not been met",
"QualityDefinitions": "Quality Definitions",

View File

@@ -1145,6 +1145,5 @@
"RottenTomatoesRating": "Tomaattiarvio",
"TotalMovies": "Elokuvia yhteensä",
"ApplicationURL": "Sovelluksen URL-osoite",
"ApplicationUrlHelpText": "Sovelluksen ulkoinen URL-osoite, johon sisältyy http(s)://, portti ja URL-perusta.",
"PreferredProtocol": "Ensisijainen protokolla"
"ApplicationUrlHelpText": "Sovelluksen ulkoinen URL-osoite, johon sisältyy http(s)://, portti ja URL-perusta."
}

View File

@@ -1145,6 +1145,5 @@
"RottenTomatoesRating": "Tomato Értékelés",
"TotalMovies": "Összes film",
"ApplicationUrlHelpText": "Az alkalmazás külső URL-címe, beleértve a \"http(s)://\"-t, a \"Portot\" és az \"URL-alapot\" is",
"ApplicationURL": "Alkalmazás URL-je",
"PreferredProtocol": "Preferált protokoll"
"ApplicationURL": "Alkalmazás URL-je"
}

View File

@@ -1006,7 +1006,7 @@
"TorrentDelayTime": "Torrent-vertraging: {0}",
"TorrentsDisabled": "Torrents uitgeschakeld",
"Trace": "Spoor",
"Trailer": "Trailer",
"Trailer": "Aanhangwagen",
"Trakt": "Trakt",
"Trigger": "In gang zetten",
"UnableToImportCheckLogs": "Gedownload - Kan niet importeren: controleer de logboeken voor details",

View File

@@ -79,6 +79,7 @@
"BranchUpdate": "更新Radarr的分支",
"Branch": "分支",
"Calendar": "日历",
"BindAddressHelpText": "192.168.50.189:7878",
"BackupRetentionHelpText": "早于保留周期的自动备份将被自动清除",
"BackupNow": "马上备份",
"BackupIntervalHelpText": "自动备份时间间隔",
@@ -195,7 +196,7 @@
"Result": "结果",
"RestoreBackup": "恢复备份",
"Restore": "恢复",
"RestartRequiredHelpTextWarning": "需要重新启动才能生效",
"RestartRequiredHelpTextWarning": "重启生效",
"RestartRadarr": "重启Radarr",
"RestartNow": "马上重启",
"Restart": "重启",
@@ -774,7 +775,7 @@
"Updates": "更新",
"UnableToLoadRestrictions": "无法加载限制条件",
"UnmonitoredHelpText": "在iCal订阅中包含未监控的电影",
"QualityProfileInUse": "无法删除已指定给影片、列表、收藏的质量配置",
"QualityProfileInUse": "无法删除已指定给影片的质量配置",
"UnableToLoadQualities": "无法加载影片质量",
"UnmappedFolders": "未映射的文件夹",
"RecycleBinCleanupDaysHelpTextWarning": "回收站中的文件在超出选择的天数后会被自动清理",
@@ -1142,9 +1143,5 @@
"ShowPosters": "显示海报",
"OnMovieAdded": "电影添加时",
"OnMovieAddedHelpText": "电影添加时",
"TotalMovies": "电影总数",
"ApplicationUrlHelpText": "此应用的外部URL包含 http(s)://端口和基本URL",
"ApplicationURL": "程序URL",
"BindAddressHelpText": "有效的 IPv4 地址或以'*'代表所有接口",
"PreferredProtocol": "首选协议"
"TotalMovies": "电影总数"
}

View File

@@ -75,7 +75,7 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
mediaInfoModel.VideoCodecID = analysis.PrimaryVideoStream?.CodecTagString;
mediaInfoModel.VideoProfile = analysis.PrimaryVideoStream?.Profile;
mediaInfoModel.VideoBitrate = analysis.PrimaryVideoStream?.BitRate ?? 0;
mediaInfoModel.VideoMultiViewCount = analysis.PrimaryVideoStream?.Tags.ContainsKey("stereo_mode") ?? false ? 2 : 1;
mediaInfoModel.VideoMultiViewCount = 1;
mediaInfoModel.VideoBitDepth = GetPixelFormat(analysis.PrimaryVideoStream?.PixelFormat)?.Components.Min(x => x.BitDepth) ?? 8;
mediaInfoModel.VideoColourPrimaries = analysis.PrimaryVideoStream?.ColorPrimaries;
mediaInfoModel.VideoTransferCharacteristics = analysis.PrimaryVideoStream?.ColorTransfer;

View File

@@ -41,12 +41,7 @@ namespace NzbDrone.Core.MediaFiles
if (RelativePath.IsNotNullOrWhiteSpace())
{
return System.IO.Path.GetFileNameWithoutExtension(RelativePath);
}
if (Path.IsNotNullOrWhiteSpace())
{
return System.IO.Path.GetFileNameWithoutExtension(Path);
return System.IO.Path.GetFileName(RelativePath);
}
return string.Empty;

View File

@@ -13,7 +13,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation
{
public interface IAggregationService
{
LocalMovie Augment(LocalMovie localMovie, DownloadClientItem downloadClientItem);
LocalMovie Augment(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles);
}
public class AggregationService : IAggregationService
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation
_logger = logger;
}
public LocalMovie Augment(LocalMovie localMovie, DownloadClientItem downloadClientItem)
public LocalMovie Augment(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
{
var isMediaFile = MediaFileExtensions.Extensions.Contains(Path.GetExtension(localMovie.Path));
@@ -52,7 +52,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation
}
localMovie.Size = _diskProvider.GetFileSize(localMovie.Path);
localMovie.SceneName = localMovie.SceneSource ? SceneNameCalculator.GetSceneName(localMovie) : null;
if (isMediaFile && (!localMovie.ExistingFile || _configService.EnableMediaInfo))
{
@@ -63,7 +62,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation
{
try
{
augmenter.Aggregate(localMovie, downloadClientItem);
augmenter.Aggregate(localMovie, downloadClientItem, otherFiles);
}
catch (Exception ex)
{

View File

@@ -6,7 +6,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
{
public class AggregateEdition : IAggregateLocalMovie
{
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem)
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
{
var movieEdition = localMovie.DownloadClientMovieInfo?.Edition;

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
_logger = logger;
}
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem)
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
{
var languages = new List<Language> { localMovie.Movie?.MovieMetadata.Value.OriginalLanguage ?? Language.Unknown };
var languagesConfidence = Confidence.Default;

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
_logger = logger;
}
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem)
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
{
var source = Source.UNKNOWN;
var sourceConfidence = Confidence.Default;

View File

@@ -6,7 +6,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
{
public class AggregateReleaseGroup : IAggregateLocalMovie
{
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem)
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
{
var releaseGroup = localMovie.DownloadClientMovieInfo?.ReleaseGroup;

View File

@@ -5,6 +5,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
{
public interface IAggregateLocalMovie
{
LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem);
LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles);
}
}

View File

@@ -118,8 +118,8 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
if (newDownload)
{
movieFile.SceneName = localMovie.SceneName;
movieFile.OriginalFilePath = GetOriginalFilePath(downloadClientItem, localMovie);
movieFile.SceneName = GetSceneName(downloadClientItem, localMovie);
var moveResult = _movieFileUpgrader.UpgradeMovieFile(movieFile, localMovie, copyOnly); //TODO: Check if this works
oldFiles = moveResult.OldFiles;
}
@@ -208,5 +208,32 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
return Path.Combine(Path.GetFileName(parentPath), Path.GetFileName(path));
}
private string GetSceneName(DownloadClientItem downloadClientItem, LocalMovie localMovie)
{
if (downloadClientItem != null)
{
var sceneNameTitle = SceneChecker.GetSceneTitle(downloadClientItem.Title);
if (sceneNameTitle != null)
{
return sceneNameTitle;
}
}
var fileName = Path.GetFileNameWithoutExtension(localMovie.Path.CleanFilePath());
var sceneNameFile = SceneChecker.GetSceneTitle(fileName);
if (sceneNameFile != null)
{
return sceneNameFile;
}
var folderTitle = localMovie.FolderMovieInfo?.ReleaseTitle;
if (folderTitle.IsNotNullOrWhiteSpace() && SceneChecker.IsSceneTitle(folderTitle))
{
return folderTitle;
}
return null;
}
}
}

View File

@@ -91,8 +91,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
FolderMovieInfo = folderInfo,
Path = file,
SceneSource = sceneSource,
ExistingFile = movie.Path.IsParentPath(file),
OtherVideoFiles = nonSampleVideoFileCount > 1
ExistingFile = movie.Path.IsParentPath(file)
};
decisions.AddIfNotNull(GetDecision(localMovie, downloadClientItem, nonSampleVideoFileCount > 1));
@@ -125,7 +124,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
try
{
_aggregationService.Augment(localMovie, downloadClientItem);
_aggregationService.Augment(localMovie, downloadClientItem, otherFiles);
if (localMovie.Movie == null)
{

View File

@@ -341,7 +341,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
localMovie.SceneSource = !existingFile;
}
localMovie = _aggregationService.Augment(localMovie, trackedDownload?.DownloadItem);
localMovie = _aggregationService.Augment(localMovie, trackedDownload?.DownloadItem, false);
// Apply the user-chosen values.
localMovie.Movie = movie;

View File

@@ -1,39 +0,0 @@
using System.IO;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.MovieImport
{
public static class SceneNameCalculator
{
public static string GetSceneName(LocalMovie localMovie)
{
var otherVideoFiles = localMovie.OtherVideoFiles;
var downloadClientInfo = localMovie.DownloadClientMovieInfo;
if (!otherVideoFiles && downloadClientInfo != null)
{
return Parser.Parser.RemoveFileExtension(downloadClientInfo.ReleaseTitle);
}
var fileName = Path.GetFileNameWithoutExtension(localMovie.Path.CleanFilePath());
if (SceneChecker.IsSceneTitle(fileName))
{
return fileName;
}
var folderTitle = localMovie.FolderMovieInfo?.ReleaseTitle;
if (!otherVideoFiles &&
folderTitle.IsNotNullOrWhiteSpace() &&
SceneChecker.IsSceneTitle(folderTitle))
{
return folderTitle;
}
return null;
}
}
}

View File

@@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.DecisionEngine;
@@ -9,16 +11,19 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
{
public class UpgradeSpecification : IImportDecisionEngineSpecification
{
private readonly ICustomFormatCalculationService _customFormatCalculationService;
private readonly Logger _logger;
public UpgradeSpecification(ICustomFormatCalculationService customFormatCalculationService, Logger logger)
{
_customFormatCalculationService = customFormatCalculationService;
_logger = logger;
}
public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem)
{
var qualityComparer = new QualityModelComparer(localMovie.Movie.Profile);
var preferredWordScore = GetCustomFormatScore(localMovie);
if (localMovie.Movie.MovieFileId > 0)
{
@@ -37,9 +42,47 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
_logger.Debug("This file isn't a quality upgrade for movie. Skipping {0}", localMovie.Path);
return Decision.Reject("Not a quality upgrade for existing movie file(s)");
}
movieFile.Movie = localMovie.Movie;
var customFormats = _customFormatCalculationService.ParseCustomFormat(movieFile);
var movieFileCustomFormatScore = localMovie.Movie.Profile.CalculateCustomFormatScore(customFormats);
if (qualityCompare == 0 && preferredWordScore < movieFileCustomFormatScore)
{
_logger.Debug("This file isn't a custom format upgrade for movie. Skipping {0}", localMovie.Path);
return Decision.Reject("Not a custom format upgrade for existing movie file(s)");
}
}
return Decision.Accept();
}
private int GetCustomFormatScore(LocalMovie localMovie)
{
var movie = localMovie.Movie;
var fileFormats = new List<CustomFormat>();
var folderFormats = new List<CustomFormat>();
var clientFormats = new List<CustomFormat>();
if (localMovie.FileMovieInfo != null)
{
fileFormats = _customFormatCalculationService.ParseCustomFormat(localMovie.FileMovieInfo, movie);
}
if (localMovie.FolderMovieInfo != null)
{
folderFormats = _customFormatCalculationService.ParseCustomFormat(localMovie.FolderMovieInfo, movie);
}
if (localMovie.DownloadClientMovieInfo != null)
{
clientFormats = _customFormatCalculationService.ParseCustomFormat(localMovie.DownloadClientMovieInfo, movie);
}
var formats = fileFormats.Union(folderFormats.Union(clientFormats)).ToList();
return movie.Profile.CalculateCustomFormatScore(formats);
}
}
}

View File

@@ -107,10 +107,6 @@ namespace NzbDrone.Core.MediaFiles
_eventAggregator.PublishEvent(new MovieFileRenamedEvent(movie, movieFile, previousPath));
}
catch (FileAlreadyExistsException ex)
{
_logger.Warn("File not renamed, there is already a file at the destination: {0}", ex.Filename);
}
catch (SameFilenameException ex)
{
_logger.Debug("File not renamed, source and destination are the same: {0}", ex.Filename);

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using NzbDrone.Core.ImportLists.ImportListMovies;
namespace NzbDrone.Core.Movies
{
@@ -12,20 +11,15 @@ namespace NzbDrone.Core.Movies
List<MovieMetadata> GetMoviesByCollectionTmdbId(int collectionId);
bool Upsert(MovieMetadata movie);
bool UpsertMany(List<MovieMetadata> movies);
void DeleteMany(List<MovieMetadata> movies);
}
public class MovieMetadataService : IMovieMetadataService
{
private readonly IMovieMetadataRepository _movieMetadataRepository;
private readonly IMovieService _movieService;
private readonly IImportListMovieService _importListMovieService;
public MovieMetadataService(IMovieMetadataRepository movieMetadataRepository, IMovieService movieService, IImportListMovieService importListMovieService)
public MovieMetadataService(IMovieMetadataRepository movieMetadataRepository)
{
_movieMetadataRepository = movieMetadataRepository;
_movieService = movieService;
_importListMovieService = importListMovieService;
}
public MovieMetadata FindByTmdbId(int tmdbId)
@@ -62,16 +56,5 @@ namespace NzbDrone.Core.Movies
{
return _movieMetadataRepository.UpsertMany(movies);
}
public void DeleteMany(List<MovieMetadata> movies)
{
foreach (var movie in movies)
{
if (!_importListMovieService.ExistsByMetadataId(movie.Id) && !_movieService.ExistsByMetadataId(movie.Id))
{
_movieMetadataRepository.Delete(movie);
}
}
}
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dapper;
using NzbDrone.Core.Datastore;
@@ -31,7 +30,6 @@ namespace NzbDrone.Core.Movies
List<int> AllMovieTmdbIds();
Dictionary<int, List<int>> AllMovieTags();
List<int> GetRecommendations();
bool ExistsByMetadataId(int metadataId);
}
public class MovieRepository : BasicRepository<Movie>, IMovieRepository
@@ -369,12 +367,5 @@ namespace NzbDrone.Core.Movies
return recommendations;
}
public bool ExistsByMetadataId(int metadataId)
{
var movies = Query(x => x.MovieMetadataId == metadataId);
return movies.Any();
}
}
}

View File

@@ -48,7 +48,6 @@ namespace NzbDrone.Core.Movies
List<int> GetRecommendedTmdbIds();
bool MoviePathExists(string folder);
void RemoveAddOptions(Movie movie);
bool ExistsByMetadataId(int metadataId);
}
public class MovieService : IMovieService, IHandle<MovieFileAddedEvent>,
@@ -390,11 +389,6 @@ namespace NzbDrone.Core.Movies
return _movieRepository.GetRecommendations();
}
public bool ExistsByMetadataId(int metadataId)
{
return _movieRepository.ExistsByMetadataId(metadataId);
}
public void Handle(MovieFileAddedEvent message)
{
var movie = message.MovieFile.Movie;

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Instrumentation.Extensions;
@@ -49,7 +48,6 @@ namespace NzbDrone.Core.Movies
_logger.ProgressInfo("Updating info for {0}", collection.Title);
MovieCollection collectionInfo;
List<MovieMetadata> movies;
try
{
@@ -70,27 +68,8 @@ namespace NzbDrone.Core.Movies
collection.LastInfoSync = DateTime.UtcNow;
collection.Images = collectionInfo.Images;
movies = collectionInfo.Movies;
movies.ForEach(x => x.CollectionTmdbId = collection.TmdbId);
var existingMetaForCollection = _movieMetadataService.GetMoviesByCollectionTmdbId(collection.TmdbId);
var updateList = new List<MovieMetadata>();
foreach (var remoteMovie in movies)
{
var existing = existingMetaForCollection.FirstOrDefault(e => e.TmdbId == remoteMovie.TmdbId);
if (existingMetaForCollection.Any(x => x.TmdbId == remoteMovie.TmdbId))
{
existingMetaForCollection.Remove(existing);
}
updateList.Add(remoteMovie);
}
_movieMetadataService.UpsertMany(updateList);
_movieMetadataService.DeleteMany(existingMetaForCollection);
collectionInfo.Movies.ForEach(x => x.CollectionTmdbId = collection.TmdbId);
_movieMetadataService.UpsertMany(collectionInfo.Movies);
_logger.Debug("Finished collection refresh for {0}", collection.Title);

View File

@@ -10,7 +10,6 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Processes;
using NzbDrone.Core.HealthCheck;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Movies;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
@@ -61,8 +60,6 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Radarr_Download_Client", message.DownloadClientName ?? string.Empty);
environmentVariables.Add("Radarr_Download_Client_Type", message.DownloadClientType ?? string.Empty);
environmentVariables.Add("Radarr_Download_Id", message.DownloadId ?? string.Empty);
environmentVariables.Add("Radarr_Release_CustomFormat", string.Join("|", remoteMovie.CustomFormats));
environmentVariables.Add("Radarr_Release_CustomFormatScore", remoteMovie.CustomFormatScore.ToString());
ExecuteScript(environmentVariables);
}
@@ -96,21 +93,11 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Radarr_Download_Client", message.DownloadClientInfo?.Name ?? string.Empty);
environmentVariables.Add("Radarr_Download_Client_Type", message.DownloadClientInfo?.Type ?? string.Empty);
environmentVariables.Add("Radarr_Download_Id", message.DownloadId ?? string.Empty);
environmentVariables.Add("Radarr_MovieFile_MediaInfo_AudioChannels", MediaInfoFormatter.FormatAudioChannels(movieFile.MediaInfo).ToString());
environmentVariables.Add("Radarr_MovieFile_MediaInfo_AudioCodec", MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, null));
environmentVariables.Add("Radarr_MovieFile_MediaInfo_AudioLanguages", movieFile.MediaInfo.AudioLanguages.Distinct().ConcatToString(" / "));
environmentVariables.Add("Radarr_MovieFile_MediaInfo_Languages", movieFile.MediaInfo.AudioLanguages.ConcatToString(" / "));
environmentVariables.Add("Radarr_MovieFile_MediaInfo_Height", movieFile.MediaInfo.Height.ToString());
environmentVariables.Add("Radarr_MovieFile_MediaInfo_Width", movieFile.MediaInfo.Width.ToString());
environmentVariables.Add("Radarr_MovieFile_MediaInfo_Subtitles", movieFile.MediaInfo.Subtitles.ConcatToString(" / "));
environmentVariables.Add("Radarr_MovieFile_MediaInfo_VideoCodec", MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, null));
environmentVariables.Add("Radarr_MovieFile_MediaInfo_VideoDynamicRangeType", MediaInfoFormatter.FormatVideoDynamicRangeType(movieFile.MediaInfo));
if (message.OldMovieFiles.Any())
{
environmentVariables.Add("Radarr_DeletedRelativePaths", string.Join("|", message.OldMovieFiles.Select(e => e.RelativePath)));
environmentVariables.Add("Radarr_DeletedPaths", string.Join("|", message.OldMovieFiles.Select(e => Path.Combine(movie.Path, e.RelativePath))));
environmentVariables.Add("Radarr_DeletedDateAdded", string.Join("|", message.OldMovieFiles.Select(e => e.DateAdded)));
}
ExecuteScript(environmentVariables);

View File

@@ -1,4 +1,3 @@
using System;
using System.Net;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
@@ -11,7 +10,6 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
public interface IPlexTvProxy
{
string GetAuthToken(string clientIdentifier, int pinId);
bool Ping(string clientIdentifier, string authToken);
}
public class PlexTvProxy : IPlexTvProxy
@@ -40,29 +38,6 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
return response.AuthToken;
}
public bool Ping(string clientIdentifier, string authToken)
{
try
{
// Allows us to tell plex.tv that we're still active and tokens should not be expired.
var request = BuildRequest(clientIdentifier);
request.ResourceUrl = "/api/v2/ping";
request.AddQueryParam("X-Plex-Token", authToken);
ProcessRequest(request);
return true;
}
catch (Exception e)
{
// Catch all exceptions and log at trace, this information could be interesting in debugging, but expired tokens will be handled elsewhere.
_logger.Trace(e, "Unable to ping plex.tv");
}
return false;
}
private HttpRequestBuilder BuildRequest(string clientIdentifier)
{
var requestBuilder = new HttpRequestBuilder("https://plex.tv")

View File

@@ -1,8 +1,5 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Text;
using NzbDrone.Common.Cache;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
@@ -14,7 +11,7 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
PlexTvPinUrlResponse GetPinUrl();
PlexTvSignInUrlResponse GetSignInUrl(string callbackUrl, int pinId, string pinCode);
string GetAuthToken(int pinId);
void Ping(string authToken);
HttpRequest GetWatchlist(string authToken);
}
@@ -22,13 +19,11 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
{
private readonly IPlexTvProxy _proxy;
private readonly IConfigService _configService;
private readonly ICached<bool> _cache;
public PlexTvService(IPlexTvProxy proxy, IConfigService configService, ICacheManager cacheManager)
public PlexTvService(IPlexTvProxy proxy, IConfigService configService)
{
_proxy = proxy;
_configService = configService;
_cache = cacheManager.GetCache<bool>(GetType());
}
public PlexTvPinUrlResponse GetPinUrl()
@@ -88,16 +83,8 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
return authToken;
}
public void Ping(string authToken)
{
// Ping plex.tv if we haven't done so in the last 24 hours for this auth token.
_cache.Get(authToken, () => _proxy.Ping(_configService.PlexClientIdentifier, authToken), TimeSpan.FromHours(24));
}
public HttpRequest GetWatchlist(string authToken)
{
Ping(authToken);
var clientIdentifier = _configService.PlexClientIdentifier;
var requestBuilder = new HttpRequestBuilder("https://metadata.provider.plex.tv/library/sections/watchlist/all")

View File

@@ -1,39 +0,0 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace NzbDrone.Core.Notifications.Plex.PlexTv
{
public class PlexSectionItemGuid
{
public string Id { get; set; }
}
public class PlexWatchlistRespone
{
[JsonProperty("Metadata")]
public List<PlexWatchlistItem> Items { get; set; }
public PlexWatchlistRespone()
{
Items = new List<PlexWatchlistItem>();
}
}
public class PlexWatchlistItem
{
public PlexWatchlistItem()
{
Guids = new List<PlexSectionItemGuid>();
}
[JsonProperty("ratingKey")]
public string Id { get; set; }
public string Title { get; set; }
public int Year { get; set; }
[JsonProperty("Guid")]
public List<PlexSectionItemGuid> Guids { get; set; }
}
}

View File

@@ -3,14 +3,22 @@ using Newtonsoft.Json;
namespace NzbDrone.Core.Notifications.Plex.Server
{
public class PlexSectionItemGuid
{
public string Id { get; set; }
}
public class PlexSectionItem
{
[JsonProperty("ratingKey")]
public string Id { get; set; }
public string Title { get; set; }
public int Year { get; set; }
public string Guid { get; set; }
[JsonProperty("Guid")]
public List<PlexSectionItemGuid> Guids { get; set; }
}
public class PlexSectionResponse
@@ -28,10 +36,5 @@ namespace NzbDrone.Core.Notifications.Plex.Server
{
[JsonProperty("_children")]
public List<PlexSectionItem> Items { get; set; }
public PlexSectionResponseLegacy()
{
Items = new List<PlexSectionItem>();
}
}
}

View File

@@ -64,8 +64,6 @@ namespace NzbDrone.Core.Notifications.Plex.Server
private void UpdateIfEnabled(Movie movie)
{
_plexTvService.Ping(Settings.AuthToken);
if (Settings.UpdateLibrary)
{
_logger.Debug("Scheduling library update for movie {0} {1}", movie.Id, movie.Title);
@@ -79,8 +77,7 @@ namespace NzbDrone.Core.Notifications.Plex.Server
public override void ProcessQueue()
{
var queue = _pendingMoviesCache.Find(Settings.Host);
PlexUpdateQueue queue = _pendingMoviesCache.Find(Settings.Host);
if (queue == null)
{
return;
@@ -133,8 +130,6 @@ namespace NzbDrone.Core.Notifications.Plex.Server
public override ValidationResult Test()
{
_plexTvService.Ping(Settings.AuthToken);
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_plexServerService.Test(Settings));

View File

@@ -1,4 +1,3 @@
using System;
using NzbDrone.Core.MediaFiles;
namespace NzbDrone.Core.Notifications.Webhook
@@ -20,8 +19,6 @@ namespace NzbDrone.Core.Notifications.Webhook
SceneName = movieFile.SceneName;
IndexerFlags = movieFile.IndexerFlags.ToString();
Size = movieFile.Size;
DateAdded = movieFile.DateAdded;
MediaInfo = new WebhookMovieFileMediaInfo(movieFile);
}
public int Id { get; set; }
@@ -33,7 +30,5 @@ namespace NzbDrone.Core.Notifications.Webhook
public string SceneName { get; set; }
public string IndexerFlags { get; set; }
public long Size { get; set; }
public DateTime DateAdded { get; set; }
public WebhookMovieFileMediaInfo MediaInfo { get; set; }
}
}

View File

@@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MediaInfo;
namespace NzbDrone.Core.Notifications.Webhook
{
public class WebhookMovieFileMediaInfo
{
public WebhookMovieFileMediaInfo()
{
}
public WebhookMovieFileMediaInfo(MovieFile movieFile)
{
AudioChannels = MediaInfoFormatter.FormatAudioChannels(movieFile.MediaInfo);
AudioCodec = MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, movieFile.SceneName);
AudioLanguages = movieFile.MediaInfo.AudioLanguages.Distinct().ToList();
Height = movieFile.MediaInfo.Height;
Width = movieFile.MediaInfo.Width;
Subtitles = movieFile.MediaInfo.Subtitles.Distinct().ToList();
VideoCodec = MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, movieFile.SceneName);
VideoDynamicRange = MediaInfoFormatter.FormatVideoDynamicRange(movieFile.MediaInfo);
VideoDynamicRangeType = MediaInfoFormatter.FormatVideoDynamicRangeType(movieFile.MediaInfo);
}
public decimal AudioChannels { get; set; }
public string AudioCodec { get; set; }
public List<string> AudioLanguages { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public List<string> Subtitles { get; set; }
public string VideoCodec { get; set; }
public string VideoDynamicRange { get; set; }
public string VideoDynamicRangeType { get; set; }
}
}

View File

@@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
@@ -19,8 +17,6 @@ namespace NzbDrone.Core.Notifications.Webhook
ReleaseTitle = remoteMovie.Release.Title;
Indexer = remoteMovie.Release.Indexer;
Size = remoteMovie.Release.Size;
CustomFormats = remoteMovie.CustomFormats?.Select(x => x.Name).ToList();
CustomFormatScore = remoteMovie.CustomFormatScore;
}
public string Quality { get; set; }
@@ -29,7 +25,5 @@ namespace NzbDrone.Core.Notifications.Webhook
public string ReleaseTitle { get; set; }
public string Indexer { get; set; }
public long Size { get; set; }
public int CustomFormatScore { get; set; }
public List<string> CustomFormats { get; set; }
}
}

View File

@@ -38,7 +38,7 @@ namespace NzbDrone.Core.Organizer
private readonly ICustomFormatService _formatService;
private readonly Logger _logger;
private static readonly Regex TitleRegex = new Regex(@"(?<tag>\{(?:imdb-|edition-))?\{(?<prefix>[- ._\[(]*)(?<token>(?:[a-z0-9]+)(?:(?<separator>[- ._]+)(?:[a-z0-9]+))?)(?::(?<customFormat>[a-z0-9|+-]+(?<!-)))?(?<suffix>[-} ._)\]]*)\}",
private static readonly Regex TitleRegex = new Regex(@"(?<tag>\{(?:imdb-|edition-))?\{(?<prefix>[- ._\[(]*)(?<token>(?:[a-z0-9]+)(?:(?<separator>[- ._]+)(?:[a-z0-9]+))?)(?::(?<customFormat>[a-z0-9|]+))?(?<suffix>[-} ._)\]]*)\}",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
public static readonly Regex MovieTitleRegex = new Regex(@"(?<token>\{((?:(Movie|Original))(?<separator>[- ._])(Clean|Original)?(Title|Filename)(The)?)(?::(?<customFormat>[a-z0-9|]+))?\})",
@@ -353,12 +353,30 @@ namespace NzbDrone.Core.Organizer
var sceneName = movieFile.GetSceneOrFileName();
var videoCodec = MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, sceneName) ?? string.Empty;
var audioCodec = MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, sceneName) ?? string.Empty;
var videoCodec = MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, sceneName);
var audioCodec = MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, sceneName);
var audioChannels = MediaInfoFormatter.FormatAudioChannels(movieFile.MediaInfo);
var audioLanguages = movieFile.MediaInfo.AudioLanguages ?? new List<string>();
var subtitles = movieFile.MediaInfo.Subtitles ?? new List<string>();
var mediaInfoAudioLanguages = GetLanguagesToken(audioLanguages);
if (!mediaInfoAudioLanguages.IsNullOrWhiteSpace())
{
mediaInfoAudioLanguages = $"[{mediaInfoAudioLanguages}]";
}
var mediaInfoAudioLanguagesAll = mediaInfoAudioLanguages;
if (mediaInfoAudioLanguages == "[EN]")
{
mediaInfoAudioLanguages = string.Empty;
}
var mediaInfoSubtitleLanguages = GetLanguagesToken(subtitles);
if (!mediaInfoSubtitleLanguages.IsNullOrWhiteSpace())
{
mediaInfoSubtitleLanguages = $"[{mediaInfoSubtitleLanguages}]";
}
var videoBitDepth = movieFile.MediaInfo.VideoBitDepth > 0 ? movieFile.MediaInfo.VideoBitDepth.ToString() : 8.ToString();
var audioChannelsFormatted = audioChannels > 0 ?
audioChannels.ToString("F1", CultureInfo.InvariantCulture) :
@@ -373,16 +391,16 @@ namespace NzbDrone.Core.Organizer
tokenHandlers["{MediaInfo Audio}"] = m => audioCodec;
tokenHandlers["{MediaInfo AudioCodec}"] = m => audioCodec;
tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannelsFormatted;
tokenHandlers["{MediaInfo AudioLanguages}"] = m => GetLanguagesToken(audioLanguages, m.CustomFormat, true, true);
tokenHandlers["{MediaInfo AudioLanguagesAll}"] = m => GetLanguagesToken(audioLanguages, m.CustomFormat, false, true);
tokenHandlers["{MediaInfo AudioLanguages}"] = m => mediaInfoAudioLanguages;
tokenHandlers["{MediaInfo AudioLanguagesAll}"] = m => mediaInfoAudioLanguagesAll;
tokenHandlers["{MediaInfo SubtitleLanguages}"] = m => GetLanguagesToken(subtitles, m.CustomFormat, false, true);
tokenHandlers["{MediaInfo SubtitleLanguagesAll}"] = m => GetLanguagesToken(subtitles, m.CustomFormat, false, true);
tokenHandlers["{MediaInfo SubtitleLanguages}"] = m => mediaInfoSubtitleLanguages;
tokenHandlers["{MediaInfo SubtitleLanguagesAll}"] = m => mediaInfoSubtitleLanguages;
tokenHandlers["{MediaInfo 3D}"] = m => mediaInfo3D;
tokenHandlers["{MediaInfo Simple}"] = m => $"{videoCodec} {audioCodec}";
tokenHandlers["{MediaInfo Full}"] = m => $"{videoCodec} {audioCodec}{GetLanguagesToken(audioLanguages, m.CustomFormat, true, true)} {GetLanguagesToken(subtitles, m.CustomFormat, false, true)}";
tokenHandlers["{MediaInfo Full}"] = m => $"{videoCodec} {audioCodec}{mediaInfoAudioLanguages} {mediaInfoSubtitleLanguages}";
tokenHandlers[MediaInfoVideoDynamicRangeToken] =
m => MediaInfoFormatter.FormatVideoDynamicRange(movieFile.MediaInfo);
@@ -401,7 +419,7 @@ namespace NzbDrone.Core.Organizer
tokenHandlers["{Custom Formats}"] = m => string.Join(" ", customFormats.Where(x => x.IncludeCustomFormatWhenRenaming));
}
private string GetLanguagesToken(List<string> mediaInfoLanguages, string filter, bool skipEnglishOnly, bool quoted)
private string GetLanguagesToken(List<string> mediaInfoLanguages)
{
var tokens = new List<string>();
foreach (var item in mediaInfoLanguages)
@@ -430,44 +448,7 @@ namespace NzbDrone.Core.Organizer
}
}
tokens = tokens.Distinct().ToList();
var filteredTokens = tokens;
// Exclude or filter
if (filter.IsNotNullOrWhiteSpace())
{
if (filter.StartsWith("-"))
{
filteredTokens = tokens.Except(filter.Split('-')).ToList();
}
else
{
filteredTokens = filter.Split('+').Intersect(tokens).ToList();
}
}
// Replace with wildcard (maybe too limited)
if (filter.IsNotNullOrWhiteSpace() && filter.EndsWith("+") && filteredTokens.Count != tokens.Count)
{
filteredTokens.Add("--");
}
if (skipEnglishOnly && filteredTokens.Count == 1 && filteredTokens.First() == "EN")
{
return string.Empty;
}
var response = string.Join("+", filteredTokens);
if (quoted && response.IsNotNullOrWhiteSpace())
{
return $"[{response}]";
}
else
{
return response;
}
return string.Join("+", tokens.Distinct());
}
private void UpdateMediaInfoIfNeeded(string pattern, MovieFile movieFile, Movie movie)

View File

@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Parser
(?<polish>\b(?:PL\W?DUB|DUB\W?PL|LEK\W?PL|PL\W?LEK)\b)|
(?<chinese>\[(?:CH[ST]|BIG5|GB)\]|||)|
(?<ukrainian>(?:(?:\dx)?UKR))|
(?<spanish>\b(?:español|castellano)\b)|
(?<latvian>\bLV\b)",
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
@@ -56,7 +55,7 @@ namespace NzbDrone.Core.Parser
languages.Add(Language.French);
}
if (lowerTitle.Contains("spanish"))
if (lowerTitle.Contains("spanish") || lowerTitle.Contains("castellano"))
{
languages.Add(Language.Spanish);
}
@@ -298,11 +297,6 @@ namespace NzbDrone.Core.Parser
languages.Add(Language.Chinese);
}
if (match.Groups["spanish"].Success)
{
languages.Add(Language.Spanish);
}
if (match.Groups["ukrainian"].Success)
{
languages.Add(Language.Ukrainian);

View File

@@ -25,8 +25,6 @@ namespace NzbDrone.Core.Parser.Model
public bool SceneSource { get; set; }
public string ReleaseGroup { get; set; }
public string Edition { get; set; }
public string SceneName { get; set; }
public bool OtherVideoFiles { get; set; }
public override string ToString()
{

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