1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-06 13:31:28 -05:00

Compare commits

..

1 Commits

Author SHA1 Message Date
ta264
7641190475 Test postgres in non-UTC docker 2022-07-17 11:38:54 +01:00
203 changed files with 1937 additions and 5853 deletions

View File

@@ -1,6 +1,7 @@
{
"paths": [
"frontend/src/**/*.js"
"frontend/src/**/*.js",
"src/NzbDrone.Core/Localization/Core/*.json"
],
"ignored": [
"**/node_modules/**/*"

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

@@ -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 bug you encountered. If a bug 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 bug you encountered.
options:
- label: I have searched the existing open and closed issues
- label: I have searched the existing issues
required: true
- type: textarea
attributes:
@@ -42,14 +42,12 @@ body:
- **Docker Install**: Yes
- **Using Reverse Proxy**: No
- **Browser**: Firefox 90 (If UI related)
- **Database**: Sqlite 3.36.0
value: |
- OS:
- Radarr:
- Docker Install:
- Using Reverse Proxy:
- Browser:
- Database:
- Radarr:
- Docker Install:
- Using Reverse Proxy:
- Browser:
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,13 +9,13 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '4.3.1'
majorVersion: '4.2.0'
minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
sentryOrg: 'servarr'
sentryUrl: 'https://sentry.servarr.com'
dotnetVersion: '6.0.400'
dotnetVersion: '6.0.300'
nodeVersion: '16.X'
innoVersion: '6.2.0'
windowsImage: 'windows-2022'
@@ -549,7 +549,7 @@ stages:
Radarr__Postgres__Password: 'radarr'
pool:
vmImage: ${{ variables.linuxImage }}
vmImage: 'ubuntu-18.04'
timeoutInMinutes: 10
@@ -687,7 +687,7 @@ stages:
Radarr__Postgres__Password: 'radarr'
pool:
vmImage: ${{ variables.linuxImage }}
vmImage: 'ubuntu-18.04'
steps:
- task: UseDotNet@2
@@ -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

@@ -223,6 +223,7 @@ module.exports = (env) => {
{
loader: 'url-loader',
options: {
limit: 24096,
mimetype: 'application/font-woff',
emitFile: false,
name: 'Content/Fonts/[name].[ext]'
@@ -232,11 +233,12 @@ module.exports = (env) => {
},
{
test: /\.(ttf|eot|eot?#iefix|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
test: /\.(ttf|eot|eot?#iefix|gif|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [
{
loader: 'file-loader',
options: {
limit: 24096,
emitFile: false,
name: 'Content/Fonts/[name].[ext]'
}

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

@@ -115,7 +115,7 @@ class EditCollectionModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>{translate('RootFolder')}</FormLabel>
<FormLabel>{translate('Folder')}</FormLabel>
<FormInputGroup
type={inputTypes.ROOT_FOLDER_SELECT}

View File

@@ -1,8 +1,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Slider from 'react-slick';
import TextTruncate from 'react-text-truncate';
import { Navigation } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react';
import EditCollectionModalConnector from 'Collection/Edit/EditCollectionModalConnector';
import CheckInput from 'Components/Form/CheckInput';
import Icon from 'Components/Icon';
@@ -18,9 +17,8 @@ import CollectionMovieConnector from './CollectionMovieConnector';
import CollectionMovieLabelConnector from './CollectionMovieLabelConnector';
import styles from './CollectionOverview.css';
// Import Swiper styles
import 'swiper/css';
import 'swiper/css/navigation';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
@@ -54,12 +52,8 @@ class CollectionOverview extends Component {
//
// Control
setSliderPrevRef = (ref) => {
this._swiperPrevRef = ref;
};
setSliderNextRef = (ref) => {
this._swiperNextRef = ref;
setSliderRef = (ref) => {
this.setState({ slider: ref });
};
//
@@ -126,6 +120,15 @@ class CollectionOverview extends Component {
const contentHeight = getContentHeight(rowHeight, isSmallScreen);
const overviewHeight = contentHeight - titleRowHeight - posterHeight;
const sliderSettings = {
arrows: false,
dots: false,
infinite: false,
slidesToShow: 1,
slidesToScroll: 1,
variableWidth: true
};
return (
<div className={styles.container}>
<div className={styles.content}>
@@ -163,21 +166,19 @@ class CollectionOverview extends Component {
{
showPosters &&
<div className={styles.navigationButtons}>
<span ref={this.setSliderPrevRef}>
<IconButton
name={icons.ARROW_LEFT}
title={translate('ScrollMovies')}
size={20}
/>
</span>
<IconButton
name={icons.ARROW_LEFT}
title={translate('ScrollMovies')}
onPress={this.state.slider?.slickPrev}
size={20}
/>
<span ref={this.setSliderNextRef}>
<IconButton
name={icons.ARROW_RIGHT}
title={translate('ScrollMovies')}
size={20}
/>
</span>
<IconButton
name={icons.ARROW_RIGHT}
title={translate('ScrollMovies')}
onPress={this.state.slider?.slickNext}
size={20}
/>
</div>
}
@@ -269,23 +270,9 @@ class CollectionOverview extends Component {
{
showPosters ?
<div className={styles.sliderContainer}>
<Swiper
slidesPerView='auto'
spaceBetween={10}
slidesPerGroup={3}
loop={false}
loopFillGroupWithBlank={true}
className="mySwiper"
modules={[Navigation]}
onInit={(swiper) => {
swiper.params.navigation.prevEl = this._swiperPrevRef;
swiper.params.navigation.nextEl = this._swiperNextRef;
swiper.navigation.init();
swiper.navigation.update();
}}
>
<Slider ref={this.setSliderRef} {...sliderSettings}>
{movies.map((movie) => (
<SwiperSlide key={movie.tmdbId} style={{ width: posterWidth }}>
<div className={styles.movie} key={movie.tmdbId}>
<CollectionMovieConnector
key={movie.tmdbId}
posterWidth={posterWidth}
@@ -294,9 +281,9 @@ class CollectionOverview extends Component {
collectionId={id}
{...movie}
/>
</SwiperSlide>
</div>
))}
</Swiper>
</Slider>
</div> :
<div className={styles.labelsContainer}>
{movies.map((movie) => (

View File

@@ -1,5 +1,4 @@
.jumpBar {
z-index: $pageJumpBarZIndex;
display: flex;
align-content: stretch;
align-items: stretch;

View File

@@ -2,8 +2,7 @@
composes: link from '~Components/Link/Link.css';
padding-top: 4px;
min-width: $toolbarButtonWidth;
width: min-content;
width: $toolbarButtonWidth;
text-align: center;
&:hover {

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

@@ -21,7 +21,6 @@ function HostSettings(props) {
port,
urlBase,
instanceName,
applicationUrl,
enableSsl,
sslPort,
sslCertPath,
@@ -91,21 +90,6 @@ function HostSettings(props) {
/>
</FormGroup>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>{translate('ApplicationURL')}</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="applicationUrl"
helpText={translate('ApplicationUrlHelpText')}
onChange={onInputChange}
{...applicationUrl}
/>
</FormGroup>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}

View File

@@ -166,7 +166,7 @@ function EditImportListModalContent(props) {
</FormGroup>
<FormGroup>
<FormLabel>{translate('RootFolder')}</FormLabel>
<FormLabel>{translate('Folder')}</FormLabel>
<FormInputGroup
type={inputTypes.ROOT_FOLDER_SELECT}

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

@@ -262,10 +262,10 @@ export const defaultState = {
type: filterBuilderTypes.ARRAY,
optionsSelector: function(items) {
const collectionList = items.reduce((acc, movie) => {
if (movie.collection && movie.collection.title) {
if (movie.collection) {
acc.push({
id: movie.collection.title,
name: movie.collection.title
id: movie.collection.name,
name: movie.collection.name
});
}

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

@@ -116,7 +116,7 @@ export const filterPredicates = {
const predicate = filterTypePredicates[type];
const { collection } = item;
return predicate(collection && collection.title ? collection.title : '', filterValue);
return predicate(collection ? collection.name : '', filterValue);
},
originalLanguage: function(item, filterValue, type) {

View File

@@ -165,11 +165,11 @@ export const actionHandlers = handleThunks({
requestData.quality = quality;
}
if (releaseGroup !== undefined) {
if (releaseGroup) {
requestData.releaseGroup = releaseGroup;
}
if (edition !== undefined) {
if (edition) {
requestData.edition = edition;
}
@@ -201,11 +201,11 @@ export const actionHandlers = handleThunks({
props.quality = quality;
}
if (edition !== undefined) {
if (edition) {
props.edition = edition;
}
if (releaseGroup !== undefined) {
if (releaseGroup) {
props.releaseGroup = releaseGroup;
}

View File

@@ -227,7 +227,7 @@ export const defaultState = {
collection: function(item) {
const { collection ={} } = item;
return collection.title;
return collection.name;
},
originalLanguage: function(item) {
@@ -339,10 +339,10 @@ export const defaultState = {
type: filterBuilderTypes.ARRAY,
optionsSelector: function(items) {
const collectionList = items.reduce((acc, movie) => {
if (movie.collection && movie.collection.title) {
if (movie.collection) {
acc.push({
id: movie.collection.title,
name: movie.collection.title
id: movie.collection.name,
name: movie.collection.name
});
}

View File

@@ -1,5 +1,4 @@
module.exports = {
pageJumpBarZIndex: 10,
modalZIndex: 1000,
popperZIndex: 2000
};

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

@@ -30,7 +30,7 @@
"@fortawesome/free-regular-svg-icons": "6.1.0",
"@fortawesome/free-solid-svg-icons": "6.1.0",
"@fortawesome/react-fontawesome": "0.1.18",
"@microsoft/signalr": "6.0.8",
"@microsoft/signalr": "6.0.5",
"@sentry/browser": "6.18.2",
"@sentry/integrations": "6.18.2",
"classnames": "2.3.1",
@@ -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",
@@ -62,7 +62,8 @@
"react-document-title": "2.0.3",
"react-dom": "17.0.2",
"react-focus-lock": "2.5.0",
"swiper": "8.3.2",
"react-slick": "0.28.1",
"slick-carousel": "1.8.1",
"react-google-recaptcha": "2.1.0",
"react-lazyload": "3.2.0",
"react-measure": "1.4.7",

View File

@@ -90,7 +90,7 @@
<!-- Standard testing packages -->
<ItemGroup Condition="'$(TestProject)'=='true'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Include="NunitXml.TestLogger" Version="3.0.117" />

View File

@@ -8,6 +8,5 @@
<add key="SQLite" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/SQLite/nuget/v3/index.json" />
<add key="coverlet-nightly" value="https://pkgs.dev.azure.com/Servarr/coverlet/_packaging/coverlet-nightly/nuget/v3/index.json" />
<add key="FFMpegCore" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/FFMpegCore/nuget/v3/index.json" />
<add key="FluentMigrator" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/FluentMigrator/nuget/v3/index.json" />
</packageSources>
</configuration>

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

@@ -64,7 +64,6 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[TestCase("https://notifiarr.com/notifier.php: api=1234530f-422f-4aac-b6b3-01233210aaaa&radarr_health_issue_message=Download")]
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;Enlist=False ***")]
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
// Announce URLs (passkeys) Magnet & Tracker
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
@@ -80,33 +79,14 @@ 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);
cleansedMessage.Should().NotContain("mySecret");
cleansedMessage.Should().NotContain("123%@%_@!#^#@");
cleansedMessage.Should().NotContain("01233210");
}
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
public void should_keep_message(string message)
{
var cleansedMessage = CleanseLogMessage.Cleanse(message);
cleansedMessage.Should().NotContain("mySecret");
cleansedMessage.Should().NotContain("123%@%_@!#^#@");
cleansedMessage.Should().NotContain("01233210");
cleansedMessage.Should().Contain("shouldkeep1");
cleansedMessage.Should().Contain("shouldkeep2");
cleansedMessage.Should().Contain("shouldkeep3");
}
[TestCase(@"Some message (from 32.2.3.5 user agent)")]
[TestCase(@"Auth-Invalidated ip 32.2.3.5")]
[TestCase(@"Auth-Success ip 32.2.3.5")]

View File

@@ -18,26 +18,9 @@ namespace NzbDrone.Common.Test.InstrumentationTests
private static LogLevel[] SentryLevels = LogLevel.AllLevels.Where(x => x >= LogLevel.Error).ToArray();
private static LogLevel[] OtherLevels = AllLevels.Except(SentryLevels).ToArray();
// TODO: SQLiteException filtering tests don't work on linux-86 and alpine customer Azure agents due to sqlite library not being loaded up, pass local
private static Exception[] FilteredExceptions = new Exception[]
{
// new SQLiteException(SQLiteErrorCode.Locked, "database is locked"),
new UnauthorizedAccessException(),
new AggregateException(new Exception[]
{
new UnauthorizedAccessException(),
new UnauthorizedAccessException()
})
};
private static Exception[] NonFilteredExceptions = new Exception[]
{
// new SQLiteException(SQLiteErrorCode.Error, "it's borked"),
new AggregateException(new Exception[]
{
new UnauthorizedAccessException(),
new NotImplementedException()
})
new UnauthorizedAccessException()
};
[SetUp]
@@ -80,14 +63,6 @@ namespace NzbDrone.Common.Test.InstrumentationTests
_subject.IsSentryMessage(log).Should().BeFalse();
}
[Test]
[TestCaseSource("NonFilteredExceptions")]
public void should_not_filter_event_for_filtered_exception_types(Exception ex)
{
var log = GivenLogEvent(LogLevel.Error, ex, "test");
_subject.IsSentryMessage(log).Should().BeTrue();
}
[Test]
[TestCaseSource("FilteredExceptions")]
public void should_not_filter_event_for_filtered_exception_types_if_filtering_disabled(Exception ex)

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

@@ -55,8 +55,7 @@ namespace NzbDrone.Common.Http
StatusCode == HttpStatusCode.Found ||
StatusCode == HttpStatusCode.TemporaryRedirect ||
StatusCode == HttpStatusCode.RedirectMethod ||
StatusCode == HttpStatusCode.SeeOther ||
StatusCode == HttpStatusCode.PermanentRedirect;
StatusCode == HttpStatusCode.SeeOther;
public string[] GetCookieHeaders()
{

View File

@@ -18,7 +18,7 @@ namespace NzbDrone.Common.Instrumentation
new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
new Regex(@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=[?& ;])[^=]*?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
new Regex(@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"),
@@ -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

@@ -229,48 +229,21 @@ namespace NzbDrone.Common.Instrumentation.Sentry
{
if (FilterEvents)
{
var exceptions = new List<Exception>();
var aggEx = logEvent.Exception as AggregateException;
if (aggEx != null && aggEx.InnerExceptions.Count > 0)
var sqlEx = logEvent.Exception as SQLiteException;
if (sqlEx != null && FilteredSQLiteErrors.Contains(sqlEx.ResultCode))
{
exceptions.AddRange(aggEx.InnerExceptions);
}
else
{
exceptions.Add(logEvent.Exception);
return false;
}
// If any are sentry then send to sentry
foreach (var ex in exceptions)
if (FilteredExceptionTypeNames.Contains(logEvent.Exception.GetType().Name))
{
var isSentry = true;
var sqlEx = ex as SQLiteException;
if (sqlEx != null && FilteredSQLiteErrors.Contains(sqlEx.ResultCode))
{
isSentry = false;
}
if (FilteredExceptionTypeNames.Contains(ex.GetType().Name))
{
isSentry = false;
}
if (FilteredExceptionMessages.Any(x => ex.Message.Contains(x)))
{
isSentry = false;
}
if (isSentry)
{
return true;
}
return false;
}
// The exception or aggregate exception children were not sentry exceptions
return false;
if (FilteredExceptionMessages.Any(x => logEvent.Exception.Message.Contains(x)))
{
return false;
}
}
return true;

View File

@@ -10,10 +10,10 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
<PackageReference Include="Sentry" Version="3.20.1" />
<PackageReference Include="Sentry" Version="3.15.0" />
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="System.Text.Json" Version="6.0.5" />
<PackageReference Include="System.Text.Json" Version="6.0.4" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />

View File

@@ -27,20 +27,6 @@ namespace NzbDrone.Core.Test.Datastore
Mocker.Resolve<IDatabase>().Vacuum();
}
[Test]
public void postgres_should_not_contain_timestamp_without_timezone_columns()
{
if (Db.DatabaseType != DatabaseType.PostgreSQL)
{
return;
}
Mocker.Resolve<IDatabase>()
.OpenConnection().Query("SELECT table_name, column_name, data_type FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = 'public' AND data_type = 'timestamp without time zone'")
.Should()
.BeNullOrEmpty();
}
[Test]
public void get_version()
{

View File

@@ -1,8 +1,6 @@
using System;
using FizzWare.NBuilder;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies;
@@ -170,80 +168,5 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Should()
.BeFalse();
}
[Test]
public void should_return_false_when_repack_but_auto_download_repack_is_false()
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotUpgrade);
_parsedMovieInfo.Quality.Revision.IsRepack = true;
_movie.MovieFileId = 1;
_movie.MovieFile = Builder<MovieFile>.CreateNew()
.With(e => e.Quality = new QualityModel(Quality.SDTV))
.With(e => e.ReleaseGroup = "Radarr")
.Build();
var remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(e => e.ParsedMovieInfo = _parsedMovieInfo)
.With(e => e.Movie = _movie)
.Build();
Subject.IsSatisfiedBy(remoteMovie, null)
.Accepted
.Should()
.BeFalse();
}
[Test]
public void should_return_true_when_repack_but_auto_download_repack_is_true()
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.PreferAndUpgrade);
_parsedMovieInfo.Quality.Revision.IsRepack = true;
_movie.MovieFileId = 1;
_movie.MovieFile = Builder<MovieFile>.CreateNew()
.With(e => e.Quality = new QualityModel(Quality.SDTV))
.With(e => e.ReleaseGroup = "Radarr")
.Build();
var remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(e => e.ParsedMovieInfo = _parsedMovieInfo)
.With(e => e.Movie = _movie)
.Build();
Subject.IsSatisfiedBy(remoteMovie, null)
.Accepted
.Should()
.BeTrue();
}
[Test]
public void should_return_true_when_repacks_are_not_preferred()
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotPrefer);
_parsedMovieInfo.Quality.Revision.IsRepack = true;
_movie.MovieFileId = 1;
_movie.MovieFile = Builder<MovieFile>.CreateNew()
.With(e => e.Quality = new QualityModel(Quality.SDTV))
.With(e => e.ReleaseGroup = "Radarr")
.Build();
var remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(e => e.ParsedMovieInfo = _parsedMovieInfo)
.With(e => e.Movie = _movie)
.Build();
Subject.IsSatisfiedBy(remoteMovie, null)
.Accepted
.Should()
.BeTrue();
}
}
}

View File

@@ -1,245 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Extras;
using NzbDrone.Core.Extras.Files;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Extras
{
[TestFixture]
public class ExtraServiceFixture : CoreTest<ExtraService>
{
private Movie _movie;
private MovieFile _movieFile;
private LocalMovie _localMovie;
private string _movieFolder;
private string _releaseFolder;
private Mock<IManageExtraFiles> _subtitleService;
private Mock<IManageExtraFiles> _otherExtraService;
[SetUp]
public void Setup()
{
_movieFolder = @"C:\Test\Movies\Movie Title".AsOsAgnostic();
_releaseFolder = @"C:\Test\Unsorted TV\Movie.Title.2022".AsOsAgnostic();
_movie = Builder<Movie>.CreateNew()
.With(s => s.Path = _movieFolder)
.Build();
_movieFile = Builder<MovieFile>.CreateNew()
.With(f => f.Path = Path.Combine(_movie.Path, "Movie Title - 2022.mkv").AsOsAgnostic())
.With(f => f.RelativePath = @"Movie Title - 2022.mkv".AsOsAgnostic())
.Build();
_localMovie = Builder<LocalMovie>.CreateNew()
.With(l => l.Movie = _movie)
.With(l => l.Path = Path.Combine(_releaseFolder, "Movie.Title.2022.mkv").AsOsAgnostic())
.Build();
_subtitleService = new Mock<IManageExtraFiles>();
_subtitleService.SetupGet(s => s.Order).Returns(0);
_subtitleService.Setup(s => s.CanImportFile(It.IsAny<LocalMovie>(), It.IsAny<MovieFile>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Returns(false);
_subtitleService.Setup(s => s.CanImportFile(It.IsAny<LocalMovie>(), It.IsAny<MovieFile>(), It.IsAny<string>(), ".srt", It.IsAny<bool>()))
.Returns(true);
_otherExtraService = new Mock<IManageExtraFiles>();
_otherExtraService.SetupGet(s => s.Order).Returns(1);
_otherExtraService.Setup(s => s.CanImportFile(It.IsAny<LocalMovie>(), It.IsAny<MovieFile>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Returns(true);
Mocker.SetConstant<IEnumerable<IManageExtraFiles>>(new[]
{
_subtitleService.Object,
_otherExtraService.Object
});
Mocker.GetMock<IDiskProvider>().Setup(s => s.FolderExists(It.IsAny<string>()))
.Returns(false);
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetParentFolder(It.IsAny<string>()))
.Returns((string path) => Directory.GetParent(path).FullName);
WithExistingFolder(_movie.Path);
WithExistingFile(_movieFile.Path);
WithExistingFile(_localMovie.Path);
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(true);
Mocker.GetMock<IConfigService>().Setup(v => v.ExtraFileExtensions).Returns("nfo,srt");
}
private void WithExistingFolder(string path, bool exists = true)
{
var dir = Path.GetDirectoryName(path);
if (exists && dir.IsNotNullOrWhiteSpace())
{
WithExistingFolder(dir);
}
Mocker.GetMock<IDiskProvider>().Setup(v => v.FolderExists(path)).Returns(exists);
}
private void WithExistingFile(string path, bool exists = true, int size = 1000)
{
var dir = Path.GetDirectoryName(path);
if (exists && dir.IsNotNullOrWhiteSpace())
{
WithExistingFolder(dir);
}
Mocker.GetMock<IDiskProvider>().Setup(v => v.FileExists(path)).Returns(exists);
Mocker.GetMock<IDiskProvider>().Setup(v => v.GetFileSize(path)).Returns(size);
}
private void WithExistingFiles(List<string> files)
{
foreach (string file in files)
{
WithExistingFile(file);
}
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(_releaseFolder, It.IsAny<SearchOption>()))
.Returns(files.ToArray());
}
[Test]
public void should_not_pass_file_if_import_disabled()
{
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(false);
var nfofile = Path.Combine(_releaseFolder, "Movie.Title.2022.nfo").AsOsAgnostic();
var files = new List<string>
{
_localMovie.Path,
nfofile
};
WithExistingFiles(files);
Subject.ImportMovie(_localMovie, _movieFile, true);
_subtitleService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
_otherExtraService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
}
[Test]
[TestCase("Movie Title - 2022.sub")]
[TestCase("Movie Title - 2022.ass")]
public void should_not_pass_unwanted_file(string filePath)
{
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(false);
var nfofile = Path.Combine(_releaseFolder, filePath).AsOsAgnostic();
var files = new List<string>
{
_localMovie.Path,
nfofile
};
WithExistingFiles(files);
Subject.ImportMovie(_localMovie, _movieFile, true);
_subtitleService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
_otherExtraService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
}
[Test]
public void should_pass_subtitle_file_to_subtitle_service()
{
var subtitleFile = Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic();
var files = new List<string>
{
_localMovie.Path,
subtitleFile
};
WithExistingFiles(files);
Subject.ImportMovie(_localMovie, _movieFile, true);
_subtitleService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true), Times.Once());
_otherExtraService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true), Times.Never());
}
[Test]
public void should_pass_nfo_file_to_other_service()
{
var nfofile = Path.Combine(_releaseFolder, "Movie.Title.2022.nfo").AsOsAgnostic();
var files = new List<string>
{
_localMovie.Path,
nfofile
};
WithExistingFiles(files);
Subject.ImportMovie(_localMovie, _movieFile, true);
_subtitleService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { nfofile }, true), Times.Never());
_otherExtraService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { nfofile }, true), Times.Once());
}
[Test]
public void should_search_subtitles_when_importing_from_job_folder()
{
_localMovie.FolderMovieInfo = new ParsedMovieInfo();
var subtitleFile = Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic();
var files = new List<string>
{
_localMovie.Path,
subtitleFile
};
WithExistingFiles(files);
Subject.ImportMovie(_localMovie, _movieFile, true);
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.AllDirectories), Times.Once);
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.TopDirectoryOnly), Times.Never);
}
[Test]
public void should_not_search_subtitles_when_not_importing_from_job_folder()
{
_localMovie.FolderMovieInfo = null;
var subtitleFile = Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic();
var files = new List<string>
{
_localMovie.Path,
subtitleFile
};
WithExistingFiles(files);
Subject.ImportMovie(_localMovie, _movieFile, true);
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.AllDirectories), Times.Never);
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.TopDirectoryOnly), Times.Once);
}
}
}

View File

@@ -1,84 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Extras.Others;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Extras.Others
{
[TestFixture]
public class OtherExtraServiceFixture : CoreTest<OtherExtraService>
{
private Movie _movie;
private MovieFile _movieFile;
private LocalMovie _localMovie;
private string _movieFolder;
private string _releaseFolder;
[SetUp]
public void Setup()
{
_movieFolder = @"C:\Test\Movies\Movie Title".AsOsAgnostic();
_releaseFolder = @"C:\Test\Unsorted Movies\Movie.Title.2022".AsOsAgnostic();
_movie = Builder<Movie>.CreateNew()
.With(s => s.Path = _movieFolder)
.Build();
_movieFile = Builder<MovieFile>.CreateNew()
.With(f => f.Path = Path.Combine(_movie.Path, "Movie Title - 2022.mkv").AsOsAgnostic())
.With(f => f.RelativePath = @"Movie Title - 2022.mkv")
.Build();
_localMovie = Builder<LocalMovie>.CreateNew()
.With(l => l.Movie = _movie)
.With(l => l.Path = Path.Combine(_releaseFolder, "Movie.Title.2022.mkv").AsOsAgnostic())
.With(l => l.FileMovieInfo = new ParsedMovieInfo
{
MovieTitles = new List<string> { "Movie Title" },
Year = 2022
})
.Build();
}
[Test]
[TestCase("Movie Title - 2022.nfo", "Movie Title - 2022.nfo")]
[TestCase("Movie.Title.2022.nfo", "Movie Title - 2022.nfo")]
[TestCase("Movie Title 2022.nfo", "Movie Title - 2022.nfo")]
[TestCase("Movie_Title_2022.nfo", "Movie Title - 2022.nfo")]
[TestCase(@"Movie.Title.2022\thumb.jpg", "Movie Title - 2022.jpg")]
public void should_import_matching_file(string filePath, string expectedOutputPath)
{
var files = new List<string> { Path.Combine(_releaseFolder, filePath).AsOsAgnostic() };
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
}
[Test]
public void should_not_import_multiple_nfo_files()
{
var files = new List<string>
{
Path.Combine(_releaseFolder, "Movie.Title.2022.nfo").AsOsAgnostic(),
Path.Combine(_releaseFolder, "Movie_Title_2022.nfo").AsOsAgnostic(),
};
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(1);
}
}
}

View File

@@ -1,179 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Extras.Subtitles;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MovieImport;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Extras.Subtitles
{
[TestFixture]
public class SubtitleServiceFixture : CoreTest<SubtitleService>
{
private Movie _movie;
private MovieFile _movieFile;
private LocalMovie _localMovie;
private string _MovieFolder;
private string _releaseFolder;
[SetUp]
public void Setup()
{
_MovieFolder = @"C:\Test\Movies\Movie Title".AsOsAgnostic();
_releaseFolder = @"C:\Test\Unsorted Movies\Movie.Title.2022".AsOsAgnostic();
_movie = Builder<Movie>.CreateNew()
.With(s => s.Path = _MovieFolder)
.Build();
_movieFile = Builder<MovieFile>.CreateNew()
.With(f => f.Path = Path.Combine(_movie.Path, "Movie Title - 2022.mkv").AsOsAgnostic())
.With(f => f.RelativePath = @"Movie Title - 2022.mkv".AsOsAgnostic())
.Build();
_localMovie = Builder<LocalMovie>.CreateNew()
.With(l => l.Movie = _movie)
.With(l => l.Path = Path.Combine(_releaseFolder, "Movie.Title.2022.mkv").AsOsAgnostic())
.With(l => l.FileMovieInfo = new ParsedMovieInfo
{
MovieTitles = new List<string> { "Movie Title" },
Year = 2022
})
.Build();
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetParentFolder(It.IsAny<string>()))
.Returns((string path) => Directory.GetParent(path).FullName);
Mocker.GetMock<IDetectSample>().Setup(s => s.IsSample(It.IsAny<MovieMetadata>(), It.IsAny<string>()))
.Returns(DetectSampleResult.NotSample);
}
[Test]
[TestCase("Movie.Title.2022.en.nfo")]
public void should_not_import_non_subtitle_file(string filePath)
{
var files = new List<string> { Path.Combine(_releaseFolder, filePath).AsOsAgnostic() };
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(0);
}
[Test]
[TestCase("Movie Title - 2022.srt", "Movie Title - 2022.srt")]
[TestCase("Movie.Title.2022.en.srt", "Movie Title - 2022.en.srt")]
[TestCase("Movie.Title.2022.english.srt", "Movie Title - 2022.en.srt")]
[TestCase("Movie Title 2022_en_sdh_forced.srt", "Movie Title - 2022.en.sdh.forced.srt")]
[TestCase("Movie_Title_2022 en.srt", "Movie Title - 2022.en.srt")]
[TestCase(@"Subs\Movie.Title.2022\2_en.srt", "Movie Title - 2022.en.srt")]
[TestCase("sub.srt", "Movie Title - 2022.srt")]
public void should_import_matching_subtitle_file(string filePath, string expectedOutputPath)
{
var files = new List<string> { Path.Combine(_releaseFolder, filePath).AsOsAgnostic() };
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
}
[Test]
public void should_import_multiple_subtitle_files_per_language()
{
var files = new List<string>
{
Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic(),
Path.Combine(_releaseFolder, "Movie.Title.2022.eng.srt").AsOsAgnostic(),
Path.Combine(_releaseFolder, "Subs", "Movie_Title_2022_en_forced.srt").AsOsAgnostic(),
Path.Combine(_releaseFolder, "Subs", "Movie.Title.2022", "2_fr.srt").AsOsAgnostic()
};
var expectedOutputs = new string[]
{
"Movie Title - 2022.1.en.srt",
"Movie Title - 2022.2.en.srt",
"Movie Title - 2022.en.forced.srt",
"Movie Title - 2022.fr.srt",
};
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(expectedOutputs.Length);
for (int i = 0; i < expectedOutputs.Length; i++)
{
results[i].RelativePath.AsOsAgnostic().PathEquals(expectedOutputs[i].AsOsAgnostic()).Should().Be(true);
}
}
[Test]
public void should_import_multiple_subtitle_files_per_language_with_tags()
{
var files = new List<string>
{
Path.Combine(_releaseFolder, "Movie.Title.2022.en.forced.cc.srt").AsOsAgnostic(),
Path.Combine(_releaseFolder, "Movie.Title.2022.other.en.forced.cc.srt").AsOsAgnostic(),
Path.Combine(_releaseFolder, "Movie.Title.2022.en.forced.sdh.srt").AsOsAgnostic(),
Path.Combine(_releaseFolder, "Movie.Title.2022.en.forced.default.srt").AsOsAgnostic(),
};
var expectedOutputs = new[]
{
"Movie Title - 2022.1.en.forced.cc.srt",
"Movie Title - 2022.2.en.forced.cc.srt",
"Movie Title - 2022.en.forced.sdh.srt",
"Movie Title - 2022.en.forced.default.srt"
};
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(expectedOutputs.Length);
for (int i = 0; i < expectedOutputs.Length; i++)
{
results[i].RelativePath.AsOsAgnostic().PathEquals(expectedOutputs[i].AsOsAgnostic()).Should().Be(true);
}
}
[Test]
[TestCase(@"Subs\2_en.srt", "Movie Title - 2022.en.srt")]
public void should_import_unmatching_subtitle_file_if_only_episode(string filePath, string expectedOutputPath)
{
var subtitleFile = Path.Combine(_releaseFolder, filePath).AsOsAgnostic();
var sampleFile = Path.Combine(_movie.Path, "Movie Title - 2022.sample.mkv").AsOsAgnostic();
var videoFiles = new string[]
{
_localMovie.Path,
sampleFile
};
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
.Returns(videoFiles);
Mocker.GetMock<IDetectSample>().Setup(s => s.IsSample(It.IsAny<MovieMetadata>(), sampleFile))
.Returns(DetectSampleResult.Sample);
var results = Subject.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true).ToList();
results.Count().Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
ExceptionVerification.ExpectedWarns(1);
}
}
}

View File

@@ -47,8 +47,6 @@ namespace NzbDrone.Core.Test.Languages
new object[] { 32, Language.Ukrainian },
new object[] { 33, Language.Persian },
new object[] { 34, Language.Bengali },
new object[] { 35, Language.Slovak },
new object[] { 36, Language.Latvian },
};
public static object[] ToIntCases =
@@ -90,8 +88,6 @@ namespace NzbDrone.Core.Test.Languages
new object[] { Language.Ukrainian, 32 },
new object[] { Language.Persian, 33 },
new object[] { Language.Bengali, 34 },
new object[] { Language.Slovak, 35 },
new object[] { Language.Latvian, 36 },
};
[Test]

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

@@ -1,67 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{
[TestFixture]
public class EditionTagsFixture : CoreTest<FileNameBuilder>
{
private Movie _movie;
private MovieFile _movieFile;
private NamingConfig _namingConfig;
[SetUp]
public void Setup()
{
_movie = Builder<Movie>
.CreateNew()
.With(s => s.Title = "South Park")
.Build();
_movieFile = new MovieFile { Quality = new QualityModel(), ReleaseGroup = "SonarrTest" };
_namingConfig = NamingConfig.Default;
_namingConfig.RenameMovies = true;
Mocker.GetMock<INamingConfigService>()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
Mocker.GetMock<IQualityDefinitionService>()
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
Mocker.GetMock<ICustomFormatService>()
.Setup(v => v.All())
.Returns(new List<CustomFormat>());
}
[Test]
public void should_add_edition_tag()
{
_movieFile.Edition = "Uncut";
_namingConfig.StandardMovieFormat = "{Movie Title} [{Edition Tags}]";
Subject.BuildFileName(_movie, _movieFile)
.Should().Be("South Park [Uncut]");
}
[TestCase("{Movie Title} {edition-{Edition Tags}}")]
public void should_conditional_hide_edition_tags_in_plex_format(string movieFormat)
{
_movieFile.Edition = "";
_namingConfig.StandardMovieFormat = movieFormat;
Subject.BuildFileName(_movie, _movieFile)
.Should().Be("South Park");
}
}
}

View File

@@ -20,7 +20,9 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{
[Platform(Exclude = "Win")]
[TestFixture]
public class FileNameBuilderFixture : CoreTest<FileNameBuilder>
{
private Movie _movie;
@@ -425,6 +427,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 +671,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,4 +1,4 @@
using FizzWare.NBuilder;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NUnit.Framework.Internal;
@@ -47,25 +47,5 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
Subject.GetMovieFolder(_movie)
.Should().Be($"Movie Title ({_movie.TmdbId})");
}
[Test]
public void should_add_imdb_tag()
{
_namingConfig.MovieFolderFormat = "{Movie Title} {imdb-{ImdbId}}";
Subject.GetMovieFolder(_movie)
.Should().Be($"Movie Title {{imdb-{_movie.ImdbId}}}");
}
[Test]
public void should_skip_imdb_tag_if_null()
{
_namingConfig.MovieFolderFormat = "{Movie Title} {imdb-{ImdbId}}";
_movie.ImdbId = null;
Subject.GetMovieFolder(_movie)
.Should().Be($"Movie Title");
}
}
}

View File

@@ -1,23 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Test.Framework;
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("[SubsPlease] Anime Title - 01 (1080p) [B1F227CF]", 1)]
[TestCase("[SubsPlease] Anime Title - 01v2 (1080p) [B1F227CF]", 2)]
[TestCase("[SubsPlease] Anime Title - 01 v2 (1080p) [B1F227CF]", 2)]
public void should_be_able_to_parse_repack(string title, int version)
{
var result = QualityParser.ParseQuality(title);
result.Revision.Version.Should().Be(version);
}
}
}

View File

@@ -27,22 +27,6 @@ namespace NzbDrone.Core.Test.ParserTests
result.Languages.Should().BeEquivalentTo(Language.Unknown);
}
[TestCase("Movie Title - 2022.en.sub")]
[TestCase("Movie Title - 2022.EN.sub")]
[TestCase("Movie Title - 2022.eng.sub")]
[TestCase("Movie Title - 2022.ENG.sub")]
[TestCase("Movie Title - 2022.English.sub")]
[TestCase("Movie Title - 2022.english.sub")]
[TestCase("Movie Title - 2022.en.cc.sub")]
[TestCase("Movie Title - 2022.en.sdh.sub")]
[TestCase("Movie Title - 2022.en.forced.sub")]
[TestCase("Movie Title - 2022.en.sdh.forced.sub")]
public void should_parse_subtitle_language_english(string fileName)
{
var result = LanguageParser.ParseSubtitleLanguage(fileName);
result.Should().Be(Language.English);
}
[TestCase("Movie.Title.1994.French.1080p.XviD-LOL")]
[TestCase("Movie Title : Other Title 2011 AVC.1080p.Blu-ray HD.VOSTFR.VFF")]
[TestCase("Movie Title - Other Title 2011 Bluray 4k HDR HEVC AC3 VFF")]
@@ -71,8 +55,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);
@@ -360,26 +342,6 @@ namespace NzbDrone.Core.Test.ParserTests
result.Languages.Should().BeEquivalentTo(Language.Bengali);
}
[TestCase("Movie.Title.1994.HDTV.x264.SK-iCZi")]
[TestCase("Movie.Title.2019.1080p.HDTV.x265.iNTERNAL.SK-iCZi")]
[TestCase("Movie.Title.2018.SLOVAK.DUAL.2160p.UHD.BluRay.x265-iCZi")]
[TestCase("Movie.Title.1990.SLOVAK.HDTV.x264-iCZi")]
public void should_parse_language_slovak(string postTitle)
{
var result = Parser.Parser.ParseMovieTitle(postTitle);
result.Languages.Should().BeEquivalentTo(Language.Slovak);
}
[TestCase("Movie.Title.2022.LV.WEBRip.XviD-LOL")]
[TestCase("Movie.Title.2022.lv.WEBRip.XviD-LOL")]
[TestCase("Movie.Title.2022.LATVIAN.WEBRip.XviD-LOL")]
[TestCase("Movie.Title.2022.Latvian.WEBRip.XviD-LOL")]
public void should_parse_language_latvian(string postTitle)
{
var result = Parser.Parser.ParseMovieTitle(postTitle);
result.Languages.Should().BeEquivalentTo(Language.Latvian);
}
[TestCase("Movie.Title.en.sub")]
[TestCase("Movie Title.eng.sub")]
[TestCase("Movie.Title.eng.forced.sub")]

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

@@ -46,7 +46,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("A.I.Artificial.Movie.(2001)", "A.I. Artificial Movie")]
[TestCase("A.Movie.Name.(1998)", "A Movie Name")]
[TestCase("www.Torrenting.com - Movie.2008.720p.X264-DIMENSION", "Movie")]
[TestCase("www.5MovieRulz.tc - Movie (2000) Malayalam HQ HDRip - x264 - AAC - 700MB.mkv", "Movie")]
[TestCase("Movie: The Movie World 2013", "Movie: The Movie World")]
[TestCase("Movie.The.Final.Chapter.2016", "Movie The Final Chapter")]
[TestCase("Der.Movie.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", "Der Movie James")]

View File

@@ -198,8 +198,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 +206,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 +314,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

@@ -50,8 +50,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("The.Movie.Title.2013.720p.BluRay.x264-ROUGH [PublicHD]", "ROUGH")]
[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);
@@ -102,11 +100,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Yet Another Anime Movie 2012 [Kametsu] [Blu-ray][MKV][h264 10-bit][1080p][FLAC 5.1][Dual Audio][Softsubs (Kametsu)]", "Kametsu")]
[TestCase("Another.Anime.Film.Name.2016.JPN.Blu-Ray.Remux.AVC.DTS-MA.BluDragon", "BluDragon")]
[TestCase("A Movie in the Name (1964) (1080p BluRay x265 r00t)", "r00t")]
[TestCase("Movie Title (2022) (2160p ATV WEB-DL Hybrid H265 DV HDR DDP Atmos 5.1 English - HONE)", "HONE")]
[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

@@ -1,161 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class SlugParserFixture : CoreTest
{
[TestCase("tèst", "test")]
[TestCase("têst", "test")]
[TestCase("tëst", "test")]
[TestCase("tËst", "test")]
[TestCase("áccent", "accent")]
[TestCase("àccent", "accent")]
[TestCase("âccent", "accent")]
[TestCase("Äccent", "accent")]
[TestCase("åccent", "accent")]
[TestCase("acceñt", "accent")]
[TestCase("ßtest", "test")]
[TestCase("œtest", "test")]
[TestCase("Œtest", "test")]
[TestCase("Øtest", "test")]
public void should_replace_accents(string input, string result)
{
Parser.Parser.ToUrlSlug(input).Should().Be(result);
}
[TestCase("Test'Result")]
[TestCase("Test$Result")]
[TestCase("Test(Result")]
[TestCase("Test)Result")]
[TestCase("Test*Result")]
[TestCase("Test?Result")]
[TestCase("Test/Result")]
[TestCase("Test=Result")]
[TestCase("Test\\Result")]
public void should_replace_special_characters(string input)
{
Parser.Parser.ToUrlSlug(input).Should().Be("testresult");
}
[TestCase("ThIS IS A MiXeD CaSe SensItIvE ValUe")]
public void should_lowercase_capitals(string input)
{
Parser.Parser.ToUrlSlug(input).Should().Be("this-is-a-mixed-case-sensitive-value");
}
[TestCase("test----")]
[TestCase("test____")]
[TestCase("test-_--_")]
public void should_trim_trailing_dashes_and_underscores(string input)
{
Parser.Parser.ToUrlSlug(input).Should().Be("test");
}
[TestCase("test result")]
[TestCase("test result")]
public void should_replace_spaces_with_dash(string input)
{
Parser.Parser.ToUrlSlug(input).Should().Be("test-result");
}
[TestCase("test result", "test-result")]
[TestCase("test-----result", "test-result")]
[TestCase("test_____result", "test_result")]
public void should_replace_double_occurence(string input, string result)
{
Parser.Parser.ToUrlSlug(input).Should().Be(result);
}
[TestCase("Test'Result")]
[TestCase("Test$Result")]
[TestCase("Test(Result")]
[TestCase("Test)Result")]
[TestCase("Test*Result")]
[TestCase("Test?Result")]
[TestCase("Test/Result")]
[TestCase("Test=Result")]
[TestCase("Test\\Result")]
public void should_replace_special_characters_with_dash_when_enabled(string input)
{
Parser.Parser.ToUrlSlug(input, true).Should().Be("test-result");
}
[TestCase("Test'Result")]
[TestCase("Test$Result")]
[TestCase("Test(Result")]
[TestCase("Test)Result")]
[TestCase("Test*Result")]
[TestCase("Test?Result")]
[TestCase("Test/Result")]
[TestCase("Test=Result")]
[TestCase("Test\\Result")]
public void should__not_replace_special_characters_with_dash_when_disabled(string input)
{
Parser.Parser.ToUrlSlug(input, false).Should().Be("testresult");
}
[TestCase("test----", "-_", "test")]
[TestCase("test____", "-_", "test")]
[TestCase("test-_-_", "-_", "test")]
[TestCase("test----", "-", "test")]
[TestCase("test____", "-", "test____")]
[TestCase("test-_-_", "-", "test-_-_")]
[TestCase("test----", "_", "test----")]
[TestCase("test____", "_", "test")]
[TestCase("test-_-_", "_", "test-_-")]
[TestCase("test----", "", "test----")]
[TestCase("test____", "", "test____")]
[TestCase("test-_-_", "", "test-_-_")]
public void should_trim_trailing_dashes_and_underscores_based_on_list(string input, string trimList, string result)
{
Parser.Parser.ToUrlSlug(input, false, trimList, "").Should().Be(result);
}
[TestCase("test----result", "-_", "test-result")]
[TestCase("test____result", "-_", "test_result")]
[TestCase("test_-_-result", "-_", "test-result")]
[TestCase("test-_-_result", "-_", "test_result")]
[TestCase("test----result", "-", "test-result")]
[TestCase("test____result", "-", "test____result")]
[TestCase("test-_-_result", "-", "test-_-_result")]
[TestCase("test----result", "_", "test----result")]
[TestCase("test____result", "_", "test_result")]
[TestCase("test-_-_result", "_", "test-_-_result")]
[TestCase("test----result", "", "test----result")]
[TestCase("test____result", "", "test____result")]
[TestCase("test-_-_result", "", "test-_-_result")]
public void should_replace_duplicate_characters_based_on_list(string input, string deduplicateChars, string result)
{
Parser.Parser.ToUrlSlug(input, false, "", deduplicateChars).Should().Be(result);
}
[Test]
public void should_handle_null_trim_parameters()
{
Parser.Parser.ToUrlSlug("test", false, null, "-_").Should().Be("test");
}
[Test]
public void should_handle_null_dedupe_parameters()
{
Parser.Parser.ToUrlSlug("test", false, "-_", null).Should().Be("test");
}
[Test]
public void should_handle_empty_trim_parameters()
{
Parser.Parser.ToUrlSlug("test", false, "", "-_").Should().Be("test");
}
[Test]
public void should_handle_empty_dedupe_parameters()
{
Parser.Parser.ToUrlSlug("test", false, "-_", "").Should().Be("test");
}
}
}

View File

@@ -8,7 +8,6 @@ using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Movies;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework;
@@ -152,107 +151,5 @@ namespace NzbDrone.Core.Test.RootFolderTests
unmappedFolders.Count.Should().BeGreaterThan(0);
unmappedFolders.Should().NotContain(u => u.Name == subFolder);
}
[TestCase("")]
[TestCase(null)]
public void should_handle_non_configured_recycle_bin(string recycleBinPath)
{
var rootFolder = Builder<RootFolder>.CreateNew()
.With(r => r.Path = @"C:\Test\TV")
.Build();
if (OsInfo.IsNotWindows)
{
rootFolder = Builder<RootFolder>.CreateNew()
.With(r => r.Path = @"/Test/TV")
.Build();
}
var subFolders = new[]
{
"Series1",
"Series2",
"Series3"
};
var folders = subFolders.Select(f => Path.Combine(@"C:\Test\TV", f)).ToArray();
if (OsInfo.IsNotWindows)
{
folders = subFolders.Select(f => Path.Combine(@"/Test/TV", f)).ToArray();
}
Mocker.GetMock<IConfigService>()
.Setup(s => s.RecycleBin)
.Returns(recycleBinPath);
Mocker.GetMock<IRootFolderRepository>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(rootFolder);
Mocker.GetMock<IMovieRepository>()
.Setup(s => s.AllMoviePaths())
.Returns(new Dictionary<int, string>());
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetDirectories(rootFolder.Path))
.Returns(folders);
var unmappedFolders = Subject.Get(rootFolder.Id, true).UnmappedFolders;
unmappedFolders.Count.Should().Be(3);
}
[Test]
public void should_exclude_recycle_bin()
{
var rootFolder = Builder<RootFolder>.CreateNew()
.With(r => r.Path = @"C:\Test\TV")
.Build();
if (OsInfo.IsNotWindows)
{
rootFolder = Builder<RootFolder>.CreateNew()
.With(r => r.Path = @"/Test/TV")
.Build();
}
var subFolders = new[]
{
"Series1",
"Series2",
"Series3",
"BIN"
};
var folders = subFolders.Select(f => Path.Combine(@"C:\Test\TV", f)).ToArray();
if (OsInfo.IsNotWindows)
{
folders = subFolders.Select(f => Path.Combine(@"/Test/TV", f)).ToArray();
}
var recycleFolder = Path.Combine(OsInfo.IsNotWindows ? @"/Test/TV" : @"C:\Test\TV", "BIN");
Mocker.GetMock<IConfigService>()
.Setup(s => s.RecycleBin)
.Returns(recycleFolder);
Mocker.GetMock<IRootFolderRepository>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(rootFolder);
Mocker.GetMock<IMovieRepository>()
.Setup(s => s.AllMoviePaths())
.Returns(new Dictionary<int, string>());
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetDirectories(rootFolder.Path))
.Returns(folders);
var unmappedFolders = Subject.Get(rootFolder.Id, true).UnmappedFolders;
unmappedFolders.Count.Should().Be(3);
unmappedFolders.Should().NotContain(u => u.Name == "BIN");
}
}
}

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

@@ -428,8 +428,6 @@ namespace NzbDrone.Core.Configuration
public CertificateValidationType CertificateValidation =>
GetValueEnum("CertificateValidation", CertificateValidationType.Enabled);
public string ApplicationUrl => GetValue("ApplicationUrl", string.Empty);
private string GetValue(string key)
{
return GetValue(key, string.Empty);

View File

@@ -104,6 +104,5 @@ namespace NzbDrone.Core.Configuration
int BackupRetention { get; }
CertificateValidationType CertificateValidation { get; }
string ApplicationUrl { get; }
}
}

View File

@@ -34,7 +34,7 @@ namespace NzbDrone.Core.CustomFormats
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo)
{
var comparedLanguage = movieInfo != null && Value == Language.Original.Id && movieInfo.ExtraInfo.ContainsKey("OriginalLanguage")
var comparedLanguage = movieInfo != null && Name == "Original" && movieInfo.ExtraInfo.ContainsKey("OriginalLanguage")
? (Language)movieInfo.ExtraInfo["OriginalLanguage"]
: (Language)Value;
return movieInfo?.Languages?.Contains(comparedLanguage) ?? false;

View File

@@ -21,7 +21,7 @@ namespace NzbDrone.Core.CustomFormats
protected Regex _regex;
protected string _raw;
[FieldDefinition(1, Label = "Regular Expression", HelpText = "Custom Format RegEx is Case Insensitive")]
[FieldDefinition(1, Label = "Regular Expression")]
public string Value
{
get => _raw;

View File

@@ -10,7 +10,7 @@ namespace NzbDrone.Core.CustomFormats
{
public SizeSpecificationValidator()
{
RuleFor(c => c.Min).GreaterThanOrEqualTo(0);
RuleFor(c => c.Min).GreaterThan(0);
RuleFor(c => c.Max).GreaterThan(c => c.Min);
}
}

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

@@ -1,57 +0,0 @@
using System;
using System.Collections.Generic;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(212)]
public class postgres_update_timestamp_columns_to_with_timezone : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Blocklist").AlterColumn("Date").AsDateTimeOffset().NotNullable();
Alter.Table("Blocklist").AlterColumn("PublishedDate").AsDateTimeOffset().Nullable();
Alter.Table("Collections").AlterColumn("Added").AsDateTimeOffset().Nullable();
Alter.Table("Collections").AlterColumn("LastInfoSync").AsDateTimeOffset().Nullable();
Alter.Table("Commands").AlterColumn("QueuedAt").AsDateTimeOffset().NotNullable();
Alter.Table("Commands").AlterColumn("StartedAt").AsDateTimeOffset().Nullable();
Alter.Table("Commands").AlterColumn("EndedAt").AsDateTimeOffset().Nullable();
Alter.Table("DownloadClientStatus").AlterColumn("InitialFailure").AsDateTimeOffset().Nullable();
Alter.Table("DownloadClientStatus").AlterColumn("MostRecentFailure").AsDateTimeOffset().Nullable();
Alter.Table("DownloadClientStatus").AlterColumn("DisabledTill").AsDateTimeOffset().Nullable();
Alter.Table("DownloadHistory").AlterColumn("Date").AsDateTimeOffset().NotNullable();
Alter.Table("ExtraFiles").AlterColumn("Added").AsDateTimeOffset().NotNullable();
Alter.Table("ExtraFiles").AlterColumn("LastUpdated").AsDateTimeOffset().NotNullable();
Alter.Table("History").AlterColumn("Date").AsDateTimeOffset().NotNullable();
Alter.Table("ImportListStatus").AlterColumn("InitialFailure").AsDateTimeOffset().Nullable();
Alter.Table("ImportListStatus").AlterColumn("MostRecentFailure").AsDateTimeOffset().Nullable();
Alter.Table("ImportListStatus").AlterColumn("DisabledTill").AsDateTimeOffset().Nullable();
Alter.Table("IndexerStatus").AlterColumn("InitialFailure").AsDateTimeOffset().Nullable();
Alter.Table("IndexerStatus").AlterColumn("MostRecentFailure").AsDateTimeOffset().Nullable();
Alter.Table("IndexerStatus").AlterColumn("DisabledTill").AsDateTimeOffset().Nullable();
Alter.Table("IndexerStatus").AlterColumn("CookiesExpirationDate").AsDateTimeOffset().Nullable();
Alter.Table("MetadataFiles").AlterColumn("LastUpdated").AsDateTimeOffset().NotNullable();
Alter.Table("MetadataFiles").AlterColumn("Added").AsDateTimeOffset().Nullable();
Alter.Table("MovieFiles").AlterColumn("DateAdded").AsDateTimeOffset().NotNullable();
Alter.Table("MovieMetadata").AlterColumn("DigitalRelease").AsDateTimeOffset().Nullable();
Alter.Table("MovieMetadata").AlterColumn("InCinemas").AsDateTimeOffset().Nullable();
Alter.Table("MovieMetadata").AlterColumn("LastInfoSync").AsDateTimeOffset().Nullable();
Alter.Table("MovieMetadata").AlterColumn("PhysicalRelease").AsDateTimeOffset().Nullable();
Alter.Table("Movies").AlterColumn("Added").AsDateTimeOffset().Nullable();
Alter.Table("PendingReleases").AlterColumn("Added").AsDateTimeOffset().NotNullable();
Alter.Table("ScheduledTasks").AlterColumn("LastExecution").AsDateTimeOffset().NotNullable();
Alter.Table("ScheduledTasks").AlterColumn("LastStartTime").AsDateTimeOffset().Nullable();
Alter.Table("SubtitleFiles").AlterColumn("Added").AsDateTimeOffset().NotNullable();
Alter.Table("SubtitleFiles").AlterColumn("LastUpdated").AsDateTimeOffset().NotNullable();
Alter.Table("VersionInfo").AlterColumn("AppliedOn").AsDateTimeOffset().Nullable();
}
protected override void LogDbUpgrade()
{
Alter.Table("Logs").AlterColumn("Time").AsDateTimeOffset().NotNullable();
Alter.Table("UpdateHistory").AlterColumn("Date").AsDateTimeOffset().NotNullable();
Alter.Table("VersionInfo").AlterColumn("AppliedOn").AsDateTimeOffset().Nullable();
}
}
}

View File

@@ -1,14 +0,0 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(214)]
public class add_language_tags_to_subtitle_files : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("SubtitleFiles").AddColumn("LanguageTags").AsString().Nullable();
}
}
}

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

@@ -1,23 +1,19 @@
using System;
using System;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class RepackSpecification : IDecisionEngineSpecification
{
private readonly UpgradableSpecification _upgradableSpecification;
private readonly IConfigService _configService;
private readonly Logger _logger;
public RepackSpecification(UpgradableSpecification upgradableSpecification, IConfigService configService, Logger logger)
public RepackSpecification(UpgradableSpecification upgradableSpecification, Logger logger)
{
_upgradableSpecification = upgradableSpecification;
_configService = configService;
_logger = logger;
}
@@ -26,19 +22,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks;
if (!subject.ParsedMovieInfo.Quality.Revision.IsRepack)
{
return Decision.Accept();
}
if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer)
{
_logger.Debug("Repacks are not preferred, skipping check");
return Decision.Accept();
}
if (subject.Movie.MovieFileId != 0)
{
var file = subject.Movie.MovieFile;
@@ -48,12 +36,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
var releaseGroup = subject.ParsedMovieInfo.ReleaseGroup;
var fileReleaseGroup = file.ReleaseGroup;
if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotUpgrade)
{
_logger.Debug("Auto downloading of repacks is disabled");
return Decision.Reject("Repack downloading is disabled");
}
if (fileReleaseGroup.IsNullOrWhiteSpace())
{
return Decision.Reject("Unable to determine release group for the existing file");

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

@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Extras.Files;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
@@ -30,6 +31,7 @@ namespace NzbDrone.Core.Extras
public void Handle(MovieScannedEvent message)
{
var movie = message.Movie;
var extraFiles = new List<ExtraFile>();
if (!_diskProvider.FolderExists(movie.Path))
{
@@ -41,16 +43,17 @@ namespace NzbDrone.Core.Extras
var filesOnDisk = _diskScanService.GetNonVideoFiles(movie.Path);
var possibleExtraFiles = _diskScanService.FilterPaths(movie.Path, filesOnDisk, false);
var filteredFiles = possibleExtraFiles;
var importedFiles = new List<string>();
foreach (var existingExtraFileImporter in _existingExtraFileImporters)
{
var imported = existingExtraFileImporter.ProcessFiles(movie, possibleExtraFiles, importedFiles);
var imported = existingExtraFileImporter.ProcessFiles(movie, filteredFiles, importedFiles);
importedFiles.AddRange(imported.Select(f => Path.Combine(movie.Path, f.RelativePath)));
}
_logger.Info("Found {0} possible extra files, imported {1} files.", possibleExtraFiles.Count, importedFiles.Count);
_logger.Info("Found {0} extra files", extraFiles.Count);
}
}
}

View File

@@ -31,6 +31,7 @@ namespace NzbDrone.Core.Extras
private readonly IDiskProvider _diskProvider;
private readonly IConfigService _configService;
private readonly List<IManageExtraFiles> _extraFileManagers;
private readonly Logger _logger;
public ExtraService(IMediaFileService mediaFileService,
IMovieService movieService,
@@ -44,6 +45,7 @@ namespace NzbDrone.Core.Extras
_diskProvider = diskProvider;
_configService = configService;
_extraFileManagers = extraFileManagers.OrderBy(e => e.Order).ToList();
_logger = logger;
}
public void ImportMovie(LocalMovie localMovie, MovieFile movieFile, bool isReadOnly)
@@ -60,42 +62,61 @@ namespace NzbDrone.Core.Extras
return;
}
var folderSearchOption = localMovie.FolderMovieInfo == null
? SearchOption.TopDirectoryOnly
: SearchOption.AllDirectories;
var sourcePath = localMovie.Path;
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
var files = _diskProvider.GetFiles(sourceFolder, SearchOption.AllDirectories).Where(f => f != localMovie.Path);
var wantedExtensions = _configService.ExtraFileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(e => e.Trim(' ', '.')
.Insert(0, "."))
.Select(e => e.Trim(' ', '.'))
.ToList();
var sourceFolder = _diskProvider.GetParentFolder(localMovie.Path);
var files = _diskProvider.GetFiles(sourceFolder, folderSearchOption);
var managedFiles = _extraFileManagers.Select((i) => new List<string>()).ToArray();
var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase)).ToList();
var filteredFilenames = new List<string>();
var hasNfo = false;
foreach (var file in files)
foreach (var matchingFilename in matchingFilenames)
{
var extension = Path.GetExtension(file);
var matchingExtension = wantedExtensions.FirstOrDefault(e => e.Equals(extension));
// Filter out duplicate NFO files
if (matchingFilename.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase))
{
if (hasNfo)
{
continue;
}
hasNfo = true;
}
filteredFilenames.Add(matchingFilename);
}
foreach (var matchingFilename in filteredFilenames)
{
var matchingExtension = wantedExtensions.FirstOrDefault(e => matchingFilename.EndsWith(e));
if (matchingExtension == null)
{
continue;
}
for (int i = 0; i < _extraFileManagers.Count; i++)
try
{
if (_extraFileManagers[i].CanImportFile(localMovie, movieFile, file, extension, isReadOnly))
foreach (var extraFileManager in _extraFileManagers)
{
managedFiles[i].Add(file);
break;
var extension = Path.GetExtension(matchingFilename);
var extraFile = extraFileManager.Import(localMovie.Movie, movieFile, matchingFilename, extension, isReadOnly);
if (extraFile != null)
{
break;
}
}
}
}
for (int i = 0; i < _extraFileManagers.Count; i++)
{
_extraFileManagers[i].ImportFiles(localMovie, movieFile, managedFiles[i], isReadOnly);
catch (Exception ex)
{
_logger.Warn(ex, "Failed to import extra file: {0}", matchingFilename);
}
}
}

View File

@@ -8,7 +8,6 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Extras.Files
{
@@ -20,8 +19,7 @@ namespace NzbDrone.Core.Extras.Files
IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
IEnumerable<ExtraFile> CreateAfterMovieFolder(Movie movie, string movieFolder);
IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly);
IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly);
ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly);
}
public abstract class ExtraFileManager<TExtraFile> : IManageExtraFiles
@@ -49,8 +47,7 @@ namespace NzbDrone.Core.Extras.Files
public abstract IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
public abstract IEnumerable<ExtraFile> CreateAfterMovieFolder(Movie movie, string movieFolder);
public abstract IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
public abstract bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly);
public abstract IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly);
public abstract ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly);
protected TExtraFile ImportFile(Movie movie, MovieFile movieFile, string path, bool readOnly, string extension, string fileNameSuffix = null)
{

View File

@@ -148,13 +148,11 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
var details = new XElement("movie");
var metadataTitle = movieTranslation?.Title ?? movie.Title;
details.Add(new XElement("title", metadataTitle));
details.Add(new XElement("title", movieTranslation?.Title ?? movie.Title));
details.Add(new XElement("originaltitle", movie.MovieMetadata.Value.OriginalTitle));
details.Add(new XElement("sorttitle", Parser.Parser.NormalizeTitle(metadataTitle)));
details.Add(new XElement("sorttitle", movie.MovieMetadata.Value.SortTitle));
if (movie.MovieMetadata.Value.Ratings.Tmdb?.Votes > 0 || movie.MovieMetadata.Value.Ratings.Imdb?.Votes > 0)
{
@@ -263,7 +261,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
details.Add(new XElement("country"));
if (Settings.AddCollectionName && movie.MovieMetadata.Value.CollectionTitle != null)
if (movie.MovieMetadata.Value.CollectionTitle != null)
{
var setElement = new XElement("set");
@@ -307,8 +305,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
details.Add(new XElement("trailer", "plugin://plugin.video.youtube/play/?video_id=" + movie.MovieMetadata.Value.YouTubeTrailerId));
details.Add(new XElement("watched", watched));
if (movieFile.MediaInfo != null)
{
var sceneName = movieFile.GetSceneOrFileName();

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