mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-13 15:34:56 -04:00
Compare commits
1 Commits
sonarr-pul
...
sonarr-pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27316b4239 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -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
|
||||
|
||||
10
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
10
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -45,11 +45,11 @@ body:
|
||||
- **Database**: Sqlite 3.36.0
|
||||
value: |
|
||||
- OS:
|
||||
- Radarr:
|
||||
- Docker Install:
|
||||
- Using Reverse Proxy:
|
||||
- Browser:
|
||||
- Database:
|
||||
- Radarr:
|
||||
- Docker Install:
|
||||
- Using Reverse Proxy:
|
||||
- Browser:
|
||||
- Database:
|
||||
render: markdown
|
||||
validations:
|
||||
required: true
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -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:
|
||||
|
||||
4
.github/workflows/azuresync.yml
vendored
4
.github/workflows/azuresync.yml
vendored
@@ -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
|
||||
|
||||
5
.github/workflows/lock.yml
vendored
5
.github/workflows/lock.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/support.yml
vendored
4
.github/workflows/support.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -9,7 +9,7 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '4.3.1'
|
||||
majorVersion: '4.2.4'
|
||||
minorVersion: $[counter('minorVersion', 2000)]
|
||||
radarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
||||
@@ -761,7 +761,7 @@ stages:
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: Packages
|
||||
itemPattern: '**/$(pattern)'
|
||||
itemPattern: '/$(pattern)'
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- bash: |
|
||||
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
|
||||
@@ -1143,5 +1143,4 @@ stages:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
DISCORDCHANNELID: $(discordChannelId)
|
||||
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
||||
DISCORDTHREADID: $(discordThreadId)
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
.modalBody {
|
||||
composes: modalBody from '~Components/Modal/ModalBody.css';
|
||||
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -36,9 +36,3 @@
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
composes: label from '~Components/Label.css';
|
||||
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
})
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')}>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -80,10 +80,6 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
// Notifiarr
|
||||
[TestCase(@"https://xxx.yyy/api/v1/notification/radarr/9pr04sg6-0123-3210-imav-eql2tyu8xyui")]
|
||||
|
||||
// Discord
|
||||
[TestCase(@"https://discord.com/api/webhooks/mySecret")]
|
||||
[TestCase(@"https://discord.com/api/webhooks/mySecret/01233210")]
|
||||
|
||||
public void should_clean_message(string message)
|
||||
{
|
||||
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -425,6 +425,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||
.Should().Be("South.Park.H264.DTS.[EN+ES+IT]");
|
||||
}
|
||||
|
||||
[Ignore("not currently supported")]
|
||||
[Test]
|
||||
public void should_format_mediainfo_3d_properly()
|
||||
{
|
||||
@@ -668,27 +669,6 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||
.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("eng/deu", "", "[EN+DE]")]
|
||||
[TestCase("eng/nld/deu", "", "[EN+NL+DE]")]
|
||||
[TestCase("eng/deu", ":DE", "[DE]")]
|
||||
[TestCase("eng/nld/deu", ":EN+NL", "[EN+NL]")]
|
||||
[TestCase("eng/nld/deu", ":NL+EN", "[NL+EN]")]
|
||||
[TestCase("eng/nld/deu", ":-NL", "[EN+DE]")]
|
||||
[TestCase("eng/nld/deu", ":DE+", "[DE+-]")]
|
||||
[TestCase("eng/nld/deu", ":DE+NO.", "[DE].")]
|
||||
[TestCase("eng/nld/deu", ":-EN-", "[NL+DE]-")]
|
||||
public void should_format_subtitle_languages_all(string subtitleLanguages, string format, string expected)
|
||||
{
|
||||
_movieFile.ReleaseGroup = null;
|
||||
|
||||
GivenMediaInfoModel(subtitles: subtitleLanguages);
|
||||
|
||||
_namingConfig.StandardMovieFormat = "{MediaInfo SubtitleLanguages" + format + "}End";
|
||||
|
||||
Subject.BuildFileName(_movie, _movieFile)
|
||||
.Should().Be(expected + "End");
|
||||
}
|
||||
|
||||
[TestCase(HdrFormat.None, "South.Park")]
|
||||
[TestCase(HdrFormat.Hlg10, "South.Park.HDR")]
|
||||
[TestCase(HdrFormat.Hdr10, "South.Park.HDR")]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
@@ -8,9 +9,9 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestFixture]
|
||||
public class AnimeVersionFixture : CoreTest
|
||||
{
|
||||
[TestCase("Anime Title - 2018 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 1)]
|
||||
[TestCase("Anime Title - 2018v2 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 2)]
|
||||
[TestCase("Anime Title - 2018 v2 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 2)]
|
||||
[TestCase("Anime Title - S01E01 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 1)]
|
||||
[TestCase("Anime Title - S01E01v2 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 2)]
|
||||
[TestCase("Anime Title - S01E01 v2 - (BD 1080p HEVC FLAC) [Dual Audio] [Group]", 2)]
|
||||
[TestCase("[SubsPlease] Anime Title - 01 (1080p) [B1F227CF]", 1)]
|
||||
[TestCase("[SubsPlease] Anime Title - 01v2 (1080p) [B1F227CF]", 2)]
|
||||
[TestCase("[SubsPlease] Anime Title - 01 v2 (1080p) [B1F227CF]", 2)]
|
||||
|
||||
@@ -71,8 +71,6 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie Title (2020)[BDRemux AVC 1080p][E-AC3 DD Plus 5.1 Castellano-Inglés Subs]")]
|
||||
[TestCase("Movie Title (2020) [UHDRemux2160p HDR][DTS-HD MA 5.1 AC3 5.1 Castellano - True-HD 7.1 Atmos Inglés Subs]")]
|
||||
[TestCase("Movie Title (2016) [UHDRemux 2160p SDR] [Castellano DD 5.1 - Inglés DTS-HD MA 5.1 Subs]")]
|
||||
[TestCase("Movie Title 2022 [HDTV 720p][Cap.101][AC3 5.1 Castellano][www.pctnew.ORG]")]
|
||||
[TestCase("Movie Title 2022 [HDTV 720p][Cap.206][AC3 5.1 Español Castellano]")]
|
||||
public void should_parse_language_spanish(string postTitle)
|
||||
{
|
||||
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@ using NzbDrone.Core.Test.Framework;
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
[TestFixture]
|
||||
|
||||
public class QualityParserFixture : CoreTest
|
||||
{
|
||||
[SetUp]
|
||||
@@ -198,8 +197,6 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie Title (2020) MULTi WEB 1080p x264-JiHEFF (S:317/L:28)", false)]
|
||||
[TestCase("Movie.Titles.2020.1080p.NF.WEB.DD2.0.x264-SNEAkY", false)]
|
||||
[TestCase("The.Movie.2022.NORDiC.1080p.DV.HDR.WEB.H 265-NiDHUG", false)]
|
||||
[TestCase("Movie Title 2018 [WEB 1080p HEVC Opus] [Netaro]", false)]
|
||||
[TestCase("Movie Title 2018 (WEB 1080p HEVC Opus) [Netaro]", false)]
|
||||
public void should_parse_webdl1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R1080p);
|
||||
@@ -208,7 +205,6 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie.Name.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", false)]
|
||||
[TestCase("Movie.Name.1x04.ITA.1080p.WEBMux.x264-NovaRip", false)]
|
||||
[TestCase("Movie.Name.2019.S02E07.Chapter.15.The.Believer.4Kto1080p.DSNYP.Webrip.x265.10bit.EAC3.5.1.Atmos.GokiTAoE", false)]
|
||||
[TestCase("Movie.Title.2019.1080p.AMZN.WEB-Rip.DDP.5.1.HEVC", false)]
|
||||
public void should_parse_webrip1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBRIP, proper, Resolution.R1080p);
|
||||
@@ -317,8 +313,6 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie.Title.M.2008.USA.BluRay.Remux.1080p.MPEG-2.DD.5.1-TDD")]
|
||||
[TestCase("Movie.Title.2018.1080p.BluRay.REMUX.MPEG-2.DTS-HD.MA.5.1-EPSiLON")]
|
||||
[TestCase("Movie.Title.II.2003.4K.BluRay.Remux.1080p.AVC.DTS-HD.MA.5.1-BMF")]
|
||||
[TestCase("Movie Title 2022 (BDRemux 1080p HEVC FLAC) [Netaro]")]
|
||||
[TestCase("[Vodes] Movie Title - Other Title (2020) [BDRemux 1080p HEVC Dual-Audio]")]
|
||||
public void should_parse_remux1080p_quality(string title)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R1080p, Modifier.REMUX);
|
||||
|
||||
@@ -51,7 +51,6 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Some.Really.Bad.Movie.Title.[2021].1080p.WEB-HDRip.Dual.Audio.[Hindi.[Clean]. .English].x264.AAC.DD.2.0.By.Full4Movies.mkv-xpost", null)]
|
||||
[TestCase("The.Movie.Title.2013.1080p.10bit.AMZN.WEB-DL.DDP5.1.HEVC-Vyndros", "Vyndros")]
|
||||
[TestCase("Movie.Name.2022.1080p.BluRay.x264-[YTS.AG]", "YTS.AG")]
|
||||
[TestCase("Movie.Title.2019.1080p.AMZN.WEB-Rip.DDP.5.1.HEVC", null)]
|
||||
public void should_parse_expected_release_group(string title, string expected)
|
||||
{
|
||||
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);
|
||||
@@ -106,7 +105,6 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie Title (2009) (2160p PMTP WEB-DL Hybrid H265 DV HDR10+ DDP Atmos 5.1 English - HONE)", "HONE")]
|
||||
[TestCase("Why.Cant.You.Use.Normal.Characters.2021.2160p.UHD.HDR10+.BluRay.TrueHD.Atmos.7.1.x265-ZØNEHD", "ZØNEHD")]
|
||||
[TestCase("Movie.Should.Not.Use.Dots.2022.1080p.BluRay.x265.10bit.Tigole", "Tigole")]
|
||||
[TestCase("Movie.Title.2005.2160p.UHD.BluRay.TrueHD 7.1.Atmos.x265 - HQMUX", "HQMUX")]
|
||||
public void should_parse_exception_release_group(string title, string expected)
|
||||
{
|
||||
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace NzbDrone.Core.HealthCheck
|
||||
.AddQueryParam("version", BuildInfo.Version)
|
||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
||||
.AddQueryParam("runtime", "netcore")
|
||||
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("branch", _configFileProvider.Branch)
|
||||
.Build();
|
||||
try
|
||||
|
||||
@@ -152,7 +152,6 @@ namespace NzbDrone.Core.History
|
||||
history.Data.Add("Guid", message.Movie.Release.Guid);
|
||||
history.Data.Add("TmdbId", message.Movie.Release.TmdbId.ToString());
|
||||
history.Data.Add("Protocol", ((int)message.Movie.Release.DownloadProtocol).ToString());
|
||||
history.Data.Add("CustomFormatScore", message.Movie.CustomFormatScore.ToString());
|
||||
history.Data.Add("IndexerFlags", message.Movie.Release.IndexerFlags.ToString());
|
||||
history.Data.Add("IndexerId", message.Movie.Release.IndexerId.ToString());
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
@@ -8,7 +7,6 @@ namespace NzbDrone.Core.ImportLists.ImportListMovies
|
||||
public interface IImportListMovieRepository : IBasicRepository<ImportListMovie>
|
||||
{
|
||||
List<ImportListMovie> GetAllForLists(List<int> listIds);
|
||||
bool ExistsByMetadataId(int metadataId);
|
||||
}
|
||||
|
||||
public class ImportListMovieRepository : BasicRepository<ImportListMovie>, IImportListMovieRepository
|
||||
@@ -22,12 +20,5 @@ namespace NzbDrone.Core.ImportLists.ImportListMovies
|
||||
{
|
||||
return Query(x => listIds.Contains(x.ListId));
|
||||
}
|
||||
|
||||
public bool ExistsByMetadataId(int metadataId)
|
||||
{
|
||||
var movies = Query(x => x.MovieMetadataId == metadataId);
|
||||
|
||||
return movies.Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace NzbDrone.Core.ImportLists.ImportListMovies
|
||||
List<ImportListMovie> SyncMoviesForList(List<ImportListMovie> listMovies, int listId);
|
||||
void RemoveListMovie(ImportListMovie listMovie);
|
||||
ImportListMovie GetById(int id);
|
||||
bool ExistsByMetadataId(int metadataId);
|
||||
}
|
||||
|
||||
public class ImportListMovieService : IImportListMovieService, IHandleAsync<ProviderDeletedEvent<IImportList>>
|
||||
@@ -80,10 +79,5 @@ namespace NzbDrone.Core.ImportLists.ImportListMovies
|
||||
var moviesOnList = _importListMovieRepository.GetAllForLists(new List<int> { message.ProviderId });
|
||||
_importListMovieRepository.DeleteMany(moviesOnList);
|
||||
}
|
||||
|
||||
public bool ExistsByMetadataId(int metadataId)
|
||||
{
|
||||
return _importListMovieRepository.ExistsByMetadataId(metadataId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.ImportLists.Exceptions;
|
||||
using NzbDrone.Core.ImportLists.ImportListMovies;
|
||||
using NzbDrone.Core.Notifications.Plex.PlexTv;
|
||||
using NzbDrone.Core.Notifications.Plex.Server;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Plex
|
||||
@@ -20,7 +19,7 @@ namespace NzbDrone.Core.ImportLists.Plex
|
||||
|
||||
public virtual IList<ImportListMovie> ParseResponse(ImportListResponse importResponse)
|
||||
{
|
||||
List<PlexWatchlistItem> items;
|
||||
List<PlexSectionItem> items;
|
||||
|
||||
_importResponse = importResponse;
|
||||
|
||||
@@ -31,7 +30,7 @@ namespace NzbDrone.Core.ImportLists.Plex
|
||||
return movies;
|
||||
}
|
||||
|
||||
items = Json.Deserialize<PlexResponse<PlexWatchlistRespone>>(_importResponse.Content)
|
||||
items = Json.Deserialize<PlexResponse<PlexSectionResponse>>(_importResponse.Content)
|
||||
.MediaContainer
|
||||
.Items;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"NoListRecommendations": "No s'han trobat elements de llista ni recomanacions; per començar, podeu afegir una pel·lícula nova, importar-ne algunes existents o afegir una llista.",
|
||||
"NotAvailable": "No disponible",
|
||||
"PreferAndUpgrade": "Marca preferit i actualitza",
|
||||
"QualityProfileInUse": "No es pot suprimir un perfil de qualitat usat a una pel·lícula, llista o col·lecció",
|
||||
"QualityProfileInUse": "No es pot suprimir un perfil de qualitat usat a una pel·lícula",
|
||||
"RadarrTags": "Etiquetes de Radarr",
|
||||
"Reddit": "Reddit",
|
||||
"RSSSyncInterval": "Interval de sincronització RSS",
|
||||
@@ -594,7 +594,7 @@
|
||||
"IndexersSettingsSummary": "Indexadors i restriccions de llançament",
|
||||
"IndexerStatusCheckAllClientMessage": "Tots els indexadors no estan disponibles a causa d'errors",
|
||||
"IndexerStatusCheckSingleClientMessage": "Els indexadors no estan disponibles a causa d'errors: {0}",
|
||||
"IndexerTagHelpText": "Utilitzeu aquest indexador només per a pel·lícules amb almenys una etiqueta coincident. Deixeu-ho en blanc per utilitzar-ho amb totes les pel·lícules.",
|
||||
"IndexerTagHelpText": "Utilitzeu aquest indexador només per a pel·lícules amb almenys una etiqueta coincident. Deixa en blanc per utilitzar-lo amb totes les pel·lícules.",
|
||||
"InstallLatest": "Instal·la l'últim",
|
||||
"LanguageHelpText": "Idioma per a llançaments",
|
||||
"Large": "Gran",
|
||||
@@ -804,7 +804,7 @@
|
||||
"RemotePathMappingCheckFilesLocalWrongOSPath": "El client de baixada local {0} ha informat de fitxers a {1}, però el camí {2} no és vàlid. Reviseu la configuració del vostre client de baixada.",
|
||||
"RemotePathMappingCheckFilesWrongOSPath": "El client de baixada remota {0} ha informat de fitxers a {1}, però el camí {2} no és vàlid. Reviseu els mapes de camins remots i baixeu la configuració del client.",
|
||||
"RemotePathMappingCheckFolderPermissions": "Radarr pot veure però no accedir al directori de descàrregues {0}. Error de permisos probable.",
|
||||
"RemotePathMappingCheckGenericPermissions": "El client de baixada {0} col·loca les baixades a {1}, però Radarr no pot veure aquest directori. És possible que hàgiu d'ajustar els permisos de la carpeta.",
|
||||
"RemotePathMappingCheckGenericPermissions": "El client de baixada {0} col·loca les baixades a {1}, però Radarr no pot veure aquest directori. És possible que hàgiu d'ajustar els permisos de la carpeta.",
|
||||
"RemotePathMappingCheckImportFailed": "Radarr no ha pogut importar una pel·lícula. Comproveu els vostres registres per obtenir més informació.",
|
||||
"RemotePathMappingCheckLocalFolderMissing": "El client de descàrrega remota {0} col·loca les baixades a {1}, però sembla que aquest directori no existeix. És probable que falti o sigui incorrecte l'assignació de camins remots.",
|
||||
"RemotePathMappingCheckLocalWrongOSPath": "El client de baixada local {0} col·loca les baixades a {1}, però el camí {2} no és vàlid. Reviseu la configuració del vostre client de baixada.",
|
||||
|
||||
@@ -339,7 +339,7 @@
|
||||
"ChangeHasNotBeenSavedYet": "Änderung wurde noch nicht gespeichert",
|
||||
"CheckForFinishedDownloadsInterval": "Intervall zum prüfen von fertigen Downloads",
|
||||
"CleanLibraryLevel": "Mediathek aufräumen",
|
||||
"ClickToChangeLanguage": "Sprache ändern ...",
|
||||
"ClickToChangeLanguage": "Sprache ändern...",
|
||||
"ClickToChangeQuality": "Hier klicken um die Qualität zu ändern",
|
||||
"ClientPriority": "Priorität",
|
||||
"CloneFormatTag": "Format Tag kopieren",
|
||||
@@ -889,7 +889,7 @@
|
||||
"RadarrUpdated": "Radarr wurde aktualisiert",
|
||||
"RadarrCalendarFeed": "Radarr Kalender Feed",
|
||||
"QueueIsEmpty": "Warteschlange ist leer",
|
||||
"QualityProfileInUse": "Ein Qualitätsprofil mit zugeordneten Filmen, Listen oder Sammlungen kann nicht gelöscht werden",
|
||||
"QualityProfileInUse": "Ein Qualitätsprofil mit zugeordneten Filmen kann nicht gelöscht werden",
|
||||
"QualityOrLangCutoffHasNotBeenMet": "Qualität oder Sprache haben die Schwelle noch nicht erreicht",
|
||||
"QualityLimitsHelpText": "Limitierungen werden automatisch an die Filmlänge angepasst.",
|
||||
"QualitiesHelpText": "Qualitätsprofile oben sind mehr bevorzugt. Profile in einer Gruppe sind gleichgestellt. Nur ausgewählte werden gesucht",
|
||||
|
||||
@@ -702,7 +702,6 @@
|
||||
"PreferIndexerFlags": "Prefer Indexer Flags",
|
||||
"PreferIndexerFlagsHelpText": "Prioritize releases with special flags",
|
||||
"Preferred": "Preferred",
|
||||
"PreferredProtocol": "Preferred Protocol",
|
||||
"PreferredSize": "Preferred Size",
|
||||
"PreferTorrent": "Prefer Torrent",
|
||||
"PreferUsenet": "Prefer Usenet",
|
||||
@@ -730,7 +729,7 @@
|
||||
"PtpOldSettingsCheckMessage": "The following PassThePopcorn indexers have deprecated settings and should be updated: {0}",
|
||||
"PublishedDate": "Published Date",
|
||||
"Qualities": "Qualities",
|
||||
"QualitiesHelpText": "Qualities higher in the list are more preferred even if not checked. Qualities within the same group are equal. Only checked qualities are wanted",
|
||||
"QualitiesHelpText": "Qualities higher in the list are more preferred. Qualities within the same group are equal. Only checked qualities are wanted",
|
||||
"Quality": "Quality",
|
||||
"QualityCutoffHasNotBeenMet": "Quality cutoff has not been met",
|
||||
"QualityDefinitions": "Quality Definitions",
|
||||
|
||||
@@ -1145,6 +1145,5 @@
|
||||
"RottenTomatoesRating": "Tomaattiarvio",
|
||||
"TotalMovies": "Elokuvia yhteensä",
|
||||
"ApplicationURL": "Sovelluksen URL-osoite",
|
||||
"ApplicationUrlHelpText": "Sovelluksen ulkoinen URL-osoite, johon sisältyy http(s)://, portti ja URL-perusta.",
|
||||
"PreferredProtocol": "Ensisijainen protokolla"
|
||||
"ApplicationUrlHelpText": "Sovelluksen ulkoinen URL-osoite, johon sisältyy http(s)://, portti ja URL-perusta."
|
||||
}
|
||||
|
||||
@@ -1145,6 +1145,5 @@
|
||||
"RottenTomatoesRating": "Tomato Értékelés",
|
||||
"TotalMovies": "Összes film",
|
||||
"ApplicationUrlHelpText": "Az alkalmazás külső URL-címe, beleértve a \"http(s)://\"-t, a \"Portot\" és az \"URL-alapot\" is",
|
||||
"ApplicationURL": "Alkalmazás URL-je",
|
||||
"PreferredProtocol": "Preferált protokoll"
|
||||
"ApplicationURL": "Alkalmazás URL-je"
|
||||
}
|
||||
|
||||
@@ -1006,7 +1006,7 @@
|
||||
"TorrentDelayTime": "Torrent-vertraging: {0}",
|
||||
"TorrentsDisabled": "Torrents uitgeschakeld",
|
||||
"Trace": "Spoor",
|
||||
"Trailer": "Trailer",
|
||||
"Trailer": "Aanhangwagen",
|
||||
"Trakt": "Trakt",
|
||||
"Trigger": "In gang zetten",
|
||||
"UnableToImportCheckLogs": "Gedownload - Kan niet importeren: controleer de logboeken voor details",
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
"BranchUpdate": "更新Radarr的分支",
|
||||
"Branch": "分支",
|
||||
"Calendar": "日历",
|
||||
"BindAddressHelpText": "192.168.50.189:7878",
|
||||
"BackupRetentionHelpText": "早于保留周期的自动备份将被自动清除",
|
||||
"BackupNow": "马上备份",
|
||||
"BackupIntervalHelpText": "自动备份时间间隔",
|
||||
@@ -195,7 +196,7 @@
|
||||
"Result": "结果",
|
||||
"RestoreBackup": "恢复备份",
|
||||
"Restore": "恢复",
|
||||
"RestartRequiredHelpTextWarning": "需要重新启动才能生效",
|
||||
"RestartRequiredHelpTextWarning": "重启生效",
|
||||
"RestartRadarr": "重启Radarr",
|
||||
"RestartNow": "马上重启",
|
||||
"Restart": "重启",
|
||||
@@ -774,7 +775,7 @@
|
||||
"Updates": "更新",
|
||||
"UnableToLoadRestrictions": "无法加载限制条件",
|
||||
"UnmonitoredHelpText": "在iCal订阅中包含未监控的电影",
|
||||
"QualityProfileInUse": "无法删除已指定给影片、列表、收藏的质量配置",
|
||||
"QualityProfileInUse": "无法删除已指定给影片的质量配置",
|
||||
"UnableToLoadQualities": "无法加载影片质量",
|
||||
"UnmappedFolders": "未映射的文件夹",
|
||||
"RecycleBinCleanupDaysHelpTextWarning": "回收站中的文件在超出选择的天数后会被自动清理",
|
||||
@@ -1142,9 +1143,5 @@
|
||||
"ShowPosters": "显示海报",
|
||||
"OnMovieAdded": "电影添加时",
|
||||
"OnMovieAddedHelpText": "电影添加时",
|
||||
"TotalMovies": "电影总数",
|
||||
"ApplicationUrlHelpText": "此应用的外部URL包含 http(s)://,端口和基本URL",
|
||||
"ApplicationURL": "程序URL",
|
||||
"BindAddressHelpText": "有效的 IPv4 地址或以'*'代表所有接口",
|
||||
"PreferredProtocol": "首选协议"
|
||||
"TotalMovies": "电影总数"
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||
mediaInfoModel.VideoCodecID = analysis.PrimaryVideoStream?.CodecTagString;
|
||||
mediaInfoModel.VideoProfile = analysis.PrimaryVideoStream?.Profile;
|
||||
mediaInfoModel.VideoBitrate = analysis.PrimaryVideoStream?.BitRate ?? 0;
|
||||
mediaInfoModel.VideoMultiViewCount = analysis.PrimaryVideoStream?.Tags.ContainsKey("stereo_mode") ?? false ? 2 : 1;
|
||||
mediaInfoModel.VideoMultiViewCount = 1;
|
||||
mediaInfoModel.VideoBitDepth = GetPixelFormat(analysis.PrimaryVideoStream?.PixelFormat)?.Components.Min(x => x.BitDepth) ?? 8;
|
||||
mediaInfoModel.VideoColourPrimaries = analysis.PrimaryVideoStream?.ColorPrimaries;
|
||||
mediaInfoModel.VideoTransferCharacteristics = analysis.PrimaryVideoStream?.ColorTransfer;
|
||||
|
||||
@@ -41,12 +41,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
|
||||
if (RelativePath.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return System.IO.Path.GetFileNameWithoutExtension(RelativePath);
|
||||
}
|
||||
|
||||
if (Path.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return System.IO.Path.GetFileNameWithoutExtension(Path);
|
||||
return System.IO.Path.GetFileName(RelativePath);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation
|
||||
{
|
||||
public interface IAggregationService
|
||||
{
|
||||
LocalMovie Augment(LocalMovie localMovie, DownloadClientItem downloadClientItem);
|
||||
LocalMovie Augment(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles);
|
||||
}
|
||||
|
||||
public class AggregationService : IAggregationService
|
||||
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public LocalMovie Augment(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
public LocalMovie Augment(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
|
||||
{
|
||||
var isMediaFile = MediaFileExtensions.Extensions.Contains(Path.GetExtension(localMovie.Path));
|
||||
|
||||
@@ -52,7 +52,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation
|
||||
}
|
||||
|
||||
localMovie.Size = _diskProvider.GetFileSize(localMovie.Path);
|
||||
localMovie.SceneName = localMovie.SceneSource ? SceneNameCalculator.GetSceneName(localMovie) : null;
|
||||
|
||||
if (isMediaFile && (!localMovie.ExistingFile || _configService.EnableMediaInfo))
|
||||
{
|
||||
@@ -63,7 +62,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation
|
||||
{
|
||||
try
|
||||
{
|
||||
augmenter.Aggregate(localMovie, downloadClientItem);
|
||||
augmenter.Aggregate(localMovie, downloadClientItem, otherFiles);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
{
|
||||
public class AggregateEdition : IAggregateLocalMovie
|
||||
{
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
|
||||
{
|
||||
var movieEdition = localMovie.DownloadClientMovieInfo?.Edition;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
|
||||
{
|
||||
var languages = new List<Language> { localMovie.Movie?.MovieMetadata.Value.OriginalLanguage ?? Language.Unknown };
|
||||
var languagesConfidence = Confidence.Default;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
|
||||
{
|
||||
var source = Source.UNKNOWN;
|
||||
var sourceConfidence = Confidence.Default;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
{
|
||||
public class AggregateReleaseGroup : IAggregateLocalMovie
|
||||
{
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
|
||||
{
|
||||
var releaseGroup = localMovie.DownloadClientMovieInfo?.ReleaseGroup;
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
{
|
||||
public interface IAggregateLocalMovie
|
||||
{
|
||||
LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem);
|
||||
LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,8 +118,8 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
|
||||
if (newDownload)
|
||||
{
|
||||
movieFile.SceneName = localMovie.SceneName;
|
||||
movieFile.OriginalFilePath = GetOriginalFilePath(downloadClientItem, localMovie);
|
||||
movieFile.SceneName = GetSceneName(downloadClientItem, localMovie);
|
||||
var moveResult = _movieFileUpgrader.UpgradeMovieFile(movieFile, localMovie, copyOnly); //TODO: Check if this works
|
||||
oldFiles = moveResult.OldFiles;
|
||||
}
|
||||
@@ -208,5 +208,32 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
|
||||
return Path.Combine(Path.GetFileName(parentPath), Path.GetFileName(path));
|
||||
}
|
||||
|
||||
private string GetSceneName(DownloadClientItem downloadClientItem, LocalMovie localMovie)
|
||||
{
|
||||
if (downloadClientItem != null)
|
||||
{
|
||||
var sceneNameTitle = SceneChecker.GetSceneTitle(downloadClientItem.Title);
|
||||
if (sceneNameTitle != null)
|
||||
{
|
||||
return sceneNameTitle;
|
||||
}
|
||||
}
|
||||
|
||||
var fileName = Path.GetFileNameWithoutExtension(localMovie.Path.CleanFilePath());
|
||||
var sceneNameFile = SceneChecker.GetSceneTitle(fileName);
|
||||
if (sceneNameFile != null)
|
||||
{
|
||||
return sceneNameFile;
|
||||
}
|
||||
|
||||
var folderTitle = localMovie.FolderMovieInfo?.ReleaseTitle;
|
||||
if (folderTitle.IsNotNullOrWhiteSpace() && SceneChecker.IsSceneTitle(folderTitle))
|
||||
{
|
||||
return folderTitle;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +91,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
FolderMovieInfo = folderInfo,
|
||||
Path = file,
|
||||
SceneSource = sceneSource,
|
||||
ExistingFile = movie.Path.IsParentPath(file),
|
||||
OtherVideoFiles = nonSampleVideoFileCount > 1
|
||||
ExistingFile = movie.Path.IsParentPath(file)
|
||||
};
|
||||
|
||||
decisions.AddIfNotNull(GetDecision(localMovie, downloadClientItem, nonSampleVideoFileCount > 1));
|
||||
@@ -125,7 +124,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
|
||||
try
|
||||
{
|
||||
_aggregationService.Augment(localMovie, downloadClientItem);
|
||||
_aggregationService.Augment(localMovie, downloadClientItem, otherFiles);
|
||||
|
||||
if (localMovie.Movie == null)
|
||||
{
|
||||
|
||||
@@ -341,7 +341,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
|
||||
localMovie.SceneSource = !existingFile;
|
||||
}
|
||||
|
||||
localMovie = _aggregationService.Augment(localMovie, trackedDownload?.DownloadItem);
|
||||
localMovie = _aggregationService.Augment(localMovie, trackedDownload?.DownloadItem, false);
|
||||
|
||||
// Apply the user-chosen values.
|
||||
localMovie.Movie = movie;
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using System.IO;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
{
|
||||
public static class SceneNameCalculator
|
||||
{
|
||||
public static string GetSceneName(LocalMovie localMovie)
|
||||
{
|
||||
var otherVideoFiles = localMovie.OtherVideoFiles;
|
||||
var downloadClientInfo = localMovie.DownloadClientMovieInfo;
|
||||
|
||||
if (!otherVideoFiles && downloadClientInfo != null)
|
||||
{
|
||||
return Parser.Parser.RemoveFileExtension(downloadClientInfo.ReleaseTitle);
|
||||
}
|
||||
|
||||
var fileName = Path.GetFileNameWithoutExtension(localMovie.Path.CleanFilePath());
|
||||
|
||||
if (SceneChecker.IsSceneTitle(fileName))
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
var folderTitle = localMovie.FolderMovieInfo?.ReleaseTitle;
|
||||
|
||||
if (!otherVideoFiles &&
|
||||
folderTitle.IsNotNullOrWhiteSpace() &&
|
||||
SceneChecker.IsSceneTitle(folderTitle))
|
||||
{
|
||||
return folderTitle;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
@@ -9,16 +11,19 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
|
||||
{
|
||||
public class UpgradeSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly ICustomFormatCalculationService _customFormatCalculationService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public UpgradeSpecification(ICustomFormatCalculationService customFormatCalculationService, Logger logger)
|
||||
{
|
||||
_customFormatCalculationService = customFormatCalculationService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
var qualityComparer = new QualityModelComparer(localMovie.Movie.Profile);
|
||||
var preferredWordScore = GetCustomFormatScore(localMovie);
|
||||
|
||||
if (localMovie.Movie.MovieFileId > 0)
|
||||
{
|
||||
@@ -37,9 +42,47 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
|
||||
_logger.Debug("This file isn't a quality upgrade for movie. Skipping {0}", localMovie.Path);
|
||||
return Decision.Reject("Not a quality upgrade for existing movie file(s)");
|
||||
}
|
||||
|
||||
movieFile.Movie = localMovie.Movie;
|
||||
|
||||
var customFormats = _customFormatCalculationService.ParseCustomFormat(movieFile);
|
||||
var movieFileCustomFormatScore = localMovie.Movie.Profile.CalculateCustomFormatScore(customFormats);
|
||||
|
||||
if (qualityCompare == 0 && preferredWordScore < movieFileCustomFormatScore)
|
||||
{
|
||||
_logger.Debug("This file isn't a custom format upgrade for movie. Skipping {0}", localMovie.Path);
|
||||
return Decision.Reject("Not a custom format upgrade for existing movie file(s)");
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
private int GetCustomFormatScore(LocalMovie localMovie)
|
||||
{
|
||||
var movie = localMovie.Movie;
|
||||
var fileFormats = new List<CustomFormat>();
|
||||
var folderFormats = new List<CustomFormat>();
|
||||
var clientFormats = new List<CustomFormat>();
|
||||
|
||||
if (localMovie.FileMovieInfo != null)
|
||||
{
|
||||
fileFormats = _customFormatCalculationService.ParseCustomFormat(localMovie.FileMovieInfo, movie);
|
||||
}
|
||||
|
||||
if (localMovie.FolderMovieInfo != null)
|
||||
{
|
||||
folderFormats = _customFormatCalculationService.ParseCustomFormat(localMovie.FolderMovieInfo, movie);
|
||||
}
|
||||
|
||||
if (localMovie.DownloadClientMovieInfo != null)
|
||||
{
|
||||
clientFormats = _customFormatCalculationService.ParseCustomFormat(localMovie.DownloadClientMovieInfo, movie);
|
||||
}
|
||||
|
||||
var formats = fileFormats.Union(folderFormats.Union(clientFormats)).ToList();
|
||||
|
||||
return movie.Profile.CalculateCustomFormatScore(formats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,10 +107,6 @@ namespace NzbDrone.Core.MediaFiles
|
||||
|
||||
_eventAggregator.PublishEvent(new MovieFileRenamedEvent(movie, movieFile, previousPath));
|
||||
}
|
||||
catch (FileAlreadyExistsException ex)
|
||||
{
|
||||
_logger.Warn("File not renamed, there is already a file at the destination: {0}", ex.Filename);
|
||||
}
|
||||
catch (SameFilenameException ex)
|
||||
{
|
||||
_logger.Debug("File not renamed, source and destination are the same: {0}", ex.Filename);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.ImportLists.ImportListMovies;
|
||||
|
||||
namespace NzbDrone.Core.Movies
|
||||
{
|
||||
@@ -12,20 +11,15 @@ namespace NzbDrone.Core.Movies
|
||||
List<MovieMetadata> GetMoviesByCollectionTmdbId(int collectionId);
|
||||
bool Upsert(MovieMetadata movie);
|
||||
bool UpsertMany(List<MovieMetadata> movies);
|
||||
void DeleteMany(List<MovieMetadata> movies);
|
||||
}
|
||||
|
||||
public class MovieMetadataService : IMovieMetadataService
|
||||
{
|
||||
private readonly IMovieMetadataRepository _movieMetadataRepository;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IImportListMovieService _importListMovieService;
|
||||
|
||||
public MovieMetadataService(IMovieMetadataRepository movieMetadataRepository, IMovieService movieService, IImportListMovieService importListMovieService)
|
||||
public MovieMetadataService(IMovieMetadataRepository movieMetadataRepository)
|
||||
{
|
||||
_movieMetadataRepository = movieMetadataRepository;
|
||||
_movieService = movieService;
|
||||
_importListMovieService = importListMovieService;
|
||||
}
|
||||
|
||||
public MovieMetadata FindByTmdbId(int tmdbId)
|
||||
@@ -62,16 +56,5 @@ namespace NzbDrone.Core.Movies
|
||||
{
|
||||
return _movieMetadataRepository.UpsertMany(movies);
|
||||
}
|
||||
|
||||
public void DeleteMany(List<MovieMetadata> movies)
|
||||
{
|
||||
foreach (var movie in movies)
|
||||
{
|
||||
if (!_importListMovieService.ExistsByMetadataId(movie.Id) && !_movieService.ExistsByMetadataId(movie.Id))
|
||||
{
|
||||
_movieMetadataRepository.Delete(movie);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
@@ -31,7 +30,6 @@ namespace NzbDrone.Core.Movies
|
||||
List<int> AllMovieTmdbIds();
|
||||
Dictionary<int, List<int>> AllMovieTags();
|
||||
List<int> GetRecommendations();
|
||||
bool ExistsByMetadataId(int metadataId);
|
||||
}
|
||||
|
||||
public class MovieRepository : BasicRepository<Movie>, IMovieRepository
|
||||
@@ -369,12 +367,5 @@ namespace NzbDrone.Core.Movies
|
||||
|
||||
return recommendations;
|
||||
}
|
||||
|
||||
public bool ExistsByMetadataId(int metadataId)
|
||||
{
|
||||
var movies = Query(x => x.MovieMetadataId == metadataId);
|
||||
|
||||
return movies.Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ namespace NzbDrone.Core.Movies
|
||||
List<int> GetRecommendedTmdbIds();
|
||||
bool MoviePathExists(string folder);
|
||||
void RemoveAddOptions(Movie movie);
|
||||
bool ExistsByMetadataId(int metadataId);
|
||||
}
|
||||
|
||||
public class MovieService : IMovieService, IHandle<MovieFileAddedEvent>,
|
||||
@@ -390,11 +389,6 @@ namespace NzbDrone.Core.Movies
|
||||
return _movieRepository.GetRecommendations();
|
||||
}
|
||||
|
||||
public bool ExistsByMetadataId(int metadataId)
|
||||
{
|
||||
return _movieRepository.ExistsByMetadataId(metadataId);
|
||||
}
|
||||
|
||||
public void Handle(MovieFileAddedEvent message)
|
||||
{
|
||||
var movie = message.MovieFile.Movie;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
@@ -49,7 +48,6 @@ namespace NzbDrone.Core.Movies
|
||||
_logger.ProgressInfo("Updating info for {0}", collection.Title);
|
||||
|
||||
MovieCollection collectionInfo;
|
||||
List<MovieMetadata> movies;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -70,27 +68,8 @@ namespace NzbDrone.Core.Movies
|
||||
collection.LastInfoSync = DateTime.UtcNow;
|
||||
collection.Images = collectionInfo.Images;
|
||||
|
||||
movies = collectionInfo.Movies;
|
||||
movies.ForEach(x => x.CollectionTmdbId = collection.TmdbId);
|
||||
|
||||
var existingMetaForCollection = _movieMetadataService.GetMoviesByCollectionTmdbId(collection.TmdbId);
|
||||
|
||||
var updateList = new List<MovieMetadata>();
|
||||
|
||||
foreach (var remoteMovie in movies)
|
||||
{
|
||||
var existing = existingMetaForCollection.FirstOrDefault(e => e.TmdbId == remoteMovie.TmdbId);
|
||||
|
||||
if (existingMetaForCollection.Any(x => x.TmdbId == remoteMovie.TmdbId))
|
||||
{
|
||||
existingMetaForCollection.Remove(existing);
|
||||
}
|
||||
|
||||
updateList.Add(remoteMovie);
|
||||
}
|
||||
|
||||
_movieMetadataService.UpsertMany(updateList);
|
||||
_movieMetadataService.DeleteMany(existingMetaForCollection);
|
||||
collectionInfo.Movies.ForEach(x => x.CollectionTmdbId = collection.TmdbId);
|
||||
_movieMetadataService.UpsertMany(collectionInfo.Movies);
|
||||
|
||||
_logger.Debug("Finished collection refresh for {0}", collection.Title);
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Processes;
|
||||
using NzbDrone.Core.HealthCheck;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -61,8 +60,6 @@ namespace NzbDrone.Core.Notifications.CustomScript
|
||||
environmentVariables.Add("Radarr_Download_Client", message.DownloadClientName ?? string.Empty);
|
||||
environmentVariables.Add("Radarr_Download_Client_Type", message.DownloadClientType ?? string.Empty);
|
||||
environmentVariables.Add("Radarr_Download_Id", message.DownloadId ?? string.Empty);
|
||||
environmentVariables.Add("Radarr_Release_CustomFormat", string.Join("|", remoteMovie.CustomFormats));
|
||||
environmentVariables.Add("Radarr_Release_CustomFormatScore", remoteMovie.CustomFormatScore.ToString());
|
||||
|
||||
ExecuteScript(environmentVariables);
|
||||
}
|
||||
@@ -96,21 +93,11 @@ namespace NzbDrone.Core.Notifications.CustomScript
|
||||
environmentVariables.Add("Radarr_Download_Client", message.DownloadClientInfo?.Name ?? string.Empty);
|
||||
environmentVariables.Add("Radarr_Download_Client_Type", message.DownloadClientInfo?.Type ?? string.Empty);
|
||||
environmentVariables.Add("Radarr_Download_Id", message.DownloadId ?? string.Empty);
|
||||
environmentVariables.Add("Radarr_MovieFile_MediaInfo_AudioChannels", MediaInfoFormatter.FormatAudioChannels(movieFile.MediaInfo).ToString());
|
||||
environmentVariables.Add("Radarr_MovieFile_MediaInfo_AudioCodec", MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, null));
|
||||
environmentVariables.Add("Radarr_MovieFile_MediaInfo_AudioLanguages", movieFile.MediaInfo.AudioLanguages.Distinct().ConcatToString(" / "));
|
||||
environmentVariables.Add("Radarr_MovieFile_MediaInfo_Languages", movieFile.MediaInfo.AudioLanguages.ConcatToString(" / "));
|
||||
environmentVariables.Add("Radarr_MovieFile_MediaInfo_Height", movieFile.MediaInfo.Height.ToString());
|
||||
environmentVariables.Add("Radarr_MovieFile_MediaInfo_Width", movieFile.MediaInfo.Width.ToString());
|
||||
environmentVariables.Add("Radarr_MovieFile_MediaInfo_Subtitles", movieFile.MediaInfo.Subtitles.ConcatToString(" / "));
|
||||
environmentVariables.Add("Radarr_MovieFile_MediaInfo_VideoCodec", MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, null));
|
||||
environmentVariables.Add("Radarr_MovieFile_MediaInfo_VideoDynamicRangeType", MediaInfoFormatter.FormatVideoDynamicRangeType(movieFile.MediaInfo));
|
||||
|
||||
if (message.OldMovieFiles.Any())
|
||||
{
|
||||
environmentVariables.Add("Radarr_DeletedRelativePaths", string.Join("|", message.OldMovieFiles.Select(e => e.RelativePath)));
|
||||
environmentVariables.Add("Radarr_DeletedPaths", string.Join("|", message.OldMovieFiles.Select(e => Path.Combine(movie.Path, e.RelativePath))));
|
||||
environmentVariables.Add("Radarr_DeletedDateAdded", string.Join("|", message.OldMovieFiles.Select(e => e.DateAdded)));
|
||||
}
|
||||
|
||||
ExecuteScript(environmentVariables);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
@@ -11,7 +10,6 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||
public interface IPlexTvProxy
|
||||
{
|
||||
string GetAuthToken(string clientIdentifier, int pinId);
|
||||
bool Ping(string clientIdentifier, string authToken);
|
||||
}
|
||||
|
||||
public class PlexTvProxy : IPlexTvProxy
|
||||
@@ -40,29 +38,6 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||
return response.AuthToken;
|
||||
}
|
||||
|
||||
public bool Ping(string clientIdentifier, string authToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Allows us to tell plex.tv that we're still active and tokens should not be expired.
|
||||
var request = BuildRequest(clientIdentifier);
|
||||
|
||||
request.ResourceUrl = "/api/v2/ping";
|
||||
request.AddQueryParam("X-Plex-Token", authToken);
|
||||
|
||||
ProcessRequest(request);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Catch all exceptions and log at trace, this information could be interesting in debugging, but expired tokens will be handled elsewhere.
|
||||
_logger.Trace(e, "Unable to ping plex.tv");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private HttpRequestBuilder BuildRequest(string clientIdentifier)
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder("https://plex.tv")
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
@@ -14,7 +11,7 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||
PlexTvPinUrlResponse GetPinUrl();
|
||||
PlexTvSignInUrlResponse GetSignInUrl(string callbackUrl, int pinId, string pinCode);
|
||||
string GetAuthToken(int pinId);
|
||||
void Ping(string authToken);
|
||||
|
||||
HttpRequest GetWatchlist(string authToken);
|
||||
}
|
||||
|
||||
@@ -22,13 +19,11 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||
{
|
||||
private readonly IPlexTvProxy _proxy;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly ICached<bool> _cache;
|
||||
|
||||
public PlexTvService(IPlexTvProxy proxy, IConfigService configService, ICacheManager cacheManager)
|
||||
public PlexTvService(IPlexTvProxy proxy, IConfigService configService)
|
||||
{
|
||||
_proxy = proxy;
|
||||
_configService = configService;
|
||||
_cache = cacheManager.GetCache<bool>(GetType());
|
||||
}
|
||||
|
||||
public PlexTvPinUrlResponse GetPinUrl()
|
||||
@@ -88,16 +83,8 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||
return authToken;
|
||||
}
|
||||
|
||||
public void Ping(string authToken)
|
||||
{
|
||||
// Ping plex.tv if we haven't done so in the last 24 hours for this auth token.
|
||||
_cache.Get(authToken, () => _proxy.Ping(_configService.PlexClientIdentifier, authToken), TimeSpan.FromHours(24));
|
||||
}
|
||||
|
||||
public HttpRequest GetWatchlist(string authToken)
|
||||
{
|
||||
Ping(authToken);
|
||||
|
||||
var clientIdentifier = _configService.PlexClientIdentifier;
|
||||
|
||||
var requestBuilder = new HttpRequestBuilder("https://metadata.provider.plex.tv/library/sections/watchlist/all")
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||
{
|
||||
public class PlexSectionItemGuid
|
||||
{
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
public class PlexWatchlistRespone
|
||||
{
|
||||
[JsonProperty("Metadata")]
|
||||
public List<PlexWatchlistItem> Items { get; set; }
|
||||
|
||||
public PlexWatchlistRespone()
|
||||
{
|
||||
Items = new List<PlexWatchlistItem>();
|
||||
}
|
||||
}
|
||||
|
||||
public class PlexWatchlistItem
|
||||
{
|
||||
public PlexWatchlistItem()
|
||||
{
|
||||
Guids = new List<PlexSectionItemGuid>();
|
||||
}
|
||||
|
||||
[JsonProperty("ratingKey")]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public int Year { get; set; }
|
||||
|
||||
[JsonProperty("Guid")]
|
||||
public List<PlexSectionItemGuid> Guids { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,22 @@ using Newtonsoft.Json;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex.Server
|
||||
{
|
||||
public class PlexSectionItemGuid
|
||||
{
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
public class PlexSectionItem
|
||||
{
|
||||
[JsonProperty("ratingKey")]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public int Year { get; set; }
|
||||
public string Guid { get; set; }
|
||||
|
||||
[JsonProperty("Guid")]
|
||||
public List<PlexSectionItemGuid> Guids { get; set; }
|
||||
}
|
||||
|
||||
public class PlexSectionResponse
|
||||
@@ -28,10 +36,5 @@ namespace NzbDrone.Core.Notifications.Plex.Server
|
||||
{
|
||||
[JsonProperty("_children")]
|
||||
public List<PlexSectionItem> Items { get; set; }
|
||||
|
||||
public PlexSectionResponseLegacy()
|
||||
{
|
||||
Items = new List<PlexSectionItem>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,8 +64,6 @@ namespace NzbDrone.Core.Notifications.Plex.Server
|
||||
|
||||
private void UpdateIfEnabled(Movie movie)
|
||||
{
|
||||
_plexTvService.Ping(Settings.AuthToken);
|
||||
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_logger.Debug("Scheduling library update for movie {0} {1}", movie.Id, movie.Title);
|
||||
@@ -79,8 +77,7 @@ namespace NzbDrone.Core.Notifications.Plex.Server
|
||||
|
||||
public override void ProcessQueue()
|
||||
{
|
||||
var queue = _pendingMoviesCache.Find(Settings.Host);
|
||||
|
||||
PlexUpdateQueue queue = _pendingMoviesCache.Find(Settings.Host);
|
||||
if (queue == null)
|
||||
{
|
||||
return;
|
||||
@@ -133,8 +130,6 @@ namespace NzbDrone.Core.Notifications.Plex.Server
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
_plexTvService.Ping(Settings.AuthToken);
|
||||
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
failures.AddIfNotNull(_plexServerService.Test(Settings));
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Webhook
|
||||
@@ -20,8 +19,6 @@ namespace NzbDrone.Core.Notifications.Webhook
|
||||
SceneName = movieFile.SceneName;
|
||||
IndexerFlags = movieFile.IndexerFlags.ToString();
|
||||
Size = movieFile.Size;
|
||||
DateAdded = movieFile.DateAdded;
|
||||
MediaInfo = new WebhookMovieFileMediaInfo(movieFile);
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
@@ -33,7 +30,5 @@ namespace NzbDrone.Core.Notifications.Webhook
|
||||
public string SceneName { get; set; }
|
||||
public string IndexerFlags { get; set; }
|
||||
public long Size { get; set; }
|
||||
public DateTime DateAdded { get; set; }
|
||||
public WebhookMovieFileMediaInfo MediaInfo { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Webhook
|
||||
{
|
||||
public class WebhookMovieFileMediaInfo
|
||||
{
|
||||
public WebhookMovieFileMediaInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookMovieFileMediaInfo(MovieFile movieFile)
|
||||
{
|
||||
AudioChannels = MediaInfoFormatter.FormatAudioChannels(movieFile.MediaInfo);
|
||||
AudioCodec = MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, movieFile.SceneName);
|
||||
AudioLanguages = movieFile.MediaInfo.AudioLanguages.Distinct().ToList();
|
||||
Height = movieFile.MediaInfo.Height;
|
||||
Width = movieFile.MediaInfo.Width;
|
||||
Subtitles = movieFile.MediaInfo.Subtitles.Distinct().ToList();
|
||||
VideoCodec = MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, movieFile.SceneName);
|
||||
VideoDynamicRange = MediaInfoFormatter.FormatVideoDynamicRange(movieFile.MediaInfo);
|
||||
VideoDynamicRangeType = MediaInfoFormatter.FormatVideoDynamicRangeType(movieFile.MediaInfo);
|
||||
}
|
||||
|
||||
public decimal AudioChannels { get; set; }
|
||||
public string AudioCodec { get; set; }
|
||||
public List<string> AudioLanguages { get; set; }
|
||||
public int Height { get; set; }
|
||||
public int Width { get; set; }
|
||||
public List<string> Subtitles { get; set; }
|
||||
public string VideoCodec { get; set; }
|
||||
public string VideoDynamicRange { get; set; }
|
||||
public string VideoDynamicRangeType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
@@ -19,8 +17,6 @@ namespace NzbDrone.Core.Notifications.Webhook
|
||||
ReleaseTitle = remoteMovie.Release.Title;
|
||||
Indexer = remoteMovie.Release.Indexer;
|
||||
Size = remoteMovie.Release.Size;
|
||||
CustomFormats = remoteMovie.CustomFormats?.Select(x => x.Name).ToList();
|
||||
CustomFormatScore = remoteMovie.CustomFormatScore;
|
||||
}
|
||||
|
||||
public string Quality { get; set; }
|
||||
@@ -29,7 +25,5 @@ namespace NzbDrone.Core.Notifications.Webhook
|
||||
public string ReleaseTitle { get; set; }
|
||||
public string Indexer { get; set; }
|
||||
public long Size { get; set; }
|
||||
public int CustomFormatScore { get; set; }
|
||||
public List<string> CustomFormats { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace NzbDrone.Core.Organizer
|
||||
private readonly ICustomFormatService _formatService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private static readonly Regex TitleRegex = new Regex(@"(?<tag>\{(?:imdb-|edition-))?\{(?<prefix>[- ._\[(]*)(?<token>(?:[a-z0-9]+)(?:(?<separator>[- ._]+)(?:[a-z0-9]+))?)(?::(?<customFormat>[a-z0-9|+-]+(?<!-)))?(?<suffix>[-} ._)\]]*)\}",
|
||||
private static readonly Regex TitleRegex = new Regex(@"(?<tag>\{(?:imdb-|edition-))?\{(?<prefix>[- ._\[(]*)(?<token>(?:[a-z0-9]+)(?:(?<separator>[- ._]+)(?:[a-z0-9]+))?)(?::(?<customFormat>[a-z0-9|]+))?(?<suffix>[-} ._)\]]*)\}",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
|
||||
public static readonly Regex MovieTitleRegex = new Regex(@"(?<token>\{((?:(Movie|Original))(?<separator>[- ._])(Clean|Original)?(Title|Filename)(The)?)(?::(?<customFormat>[a-z0-9|]+))?\})",
|
||||
@@ -353,12 +353,30 @@ namespace NzbDrone.Core.Organizer
|
||||
|
||||
var sceneName = movieFile.GetSceneOrFileName();
|
||||
|
||||
var videoCodec = MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, sceneName) ?? string.Empty;
|
||||
var audioCodec = MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, sceneName) ?? string.Empty;
|
||||
var videoCodec = MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, sceneName);
|
||||
var audioCodec = MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, sceneName);
|
||||
var audioChannels = MediaInfoFormatter.FormatAudioChannels(movieFile.MediaInfo);
|
||||
var audioLanguages = movieFile.MediaInfo.AudioLanguages ?? new List<string>();
|
||||
var subtitles = movieFile.MediaInfo.Subtitles ?? new List<string>();
|
||||
|
||||
var mediaInfoAudioLanguages = GetLanguagesToken(audioLanguages);
|
||||
if (!mediaInfoAudioLanguages.IsNullOrWhiteSpace())
|
||||
{
|
||||
mediaInfoAudioLanguages = $"[{mediaInfoAudioLanguages}]";
|
||||
}
|
||||
|
||||
var mediaInfoAudioLanguagesAll = mediaInfoAudioLanguages;
|
||||
if (mediaInfoAudioLanguages == "[EN]")
|
||||
{
|
||||
mediaInfoAudioLanguages = string.Empty;
|
||||
}
|
||||
|
||||
var mediaInfoSubtitleLanguages = GetLanguagesToken(subtitles);
|
||||
if (!mediaInfoSubtitleLanguages.IsNullOrWhiteSpace())
|
||||
{
|
||||
mediaInfoSubtitleLanguages = $"[{mediaInfoSubtitleLanguages}]";
|
||||
}
|
||||
|
||||
var videoBitDepth = movieFile.MediaInfo.VideoBitDepth > 0 ? movieFile.MediaInfo.VideoBitDepth.ToString() : 8.ToString();
|
||||
var audioChannelsFormatted = audioChannels > 0 ?
|
||||
audioChannels.ToString("F1", CultureInfo.InvariantCulture) :
|
||||
@@ -373,16 +391,16 @@ namespace NzbDrone.Core.Organizer
|
||||
tokenHandlers["{MediaInfo Audio}"] = m => audioCodec;
|
||||
tokenHandlers["{MediaInfo AudioCodec}"] = m => audioCodec;
|
||||
tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannelsFormatted;
|
||||
tokenHandlers["{MediaInfo AudioLanguages}"] = m => GetLanguagesToken(audioLanguages, m.CustomFormat, true, true);
|
||||
tokenHandlers["{MediaInfo AudioLanguagesAll}"] = m => GetLanguagesToken(audioLanguages, m.CustomFormat, false, true);
|
||||
tokenHandlers["{MediaInfo AudioLanguages}"] = m => mediaInfoAudioLanguages;
|
||||
tokenHandlers["{MediaInfo AudioLanguagesAll}"] = m => mediaInfoAudioLanguagesAll;
|
||||
|
||||
tokenHandlers["{MediaInfo SubtitleLanguages}"] = m => GetLanguagesToken(subtitles, m.CustomFormat, false, true);
|
||||
tokenHandlers["{MediaInfo SubtitleLanguagesAll}"] = m => GetLanguagesToken(subtitles, m.CustomFormat, false, true);
|
||||
tokenHandlers["{MediaInfo SubtitleLanguages}"] = m => mediaInfoSubtitleLanguages;
|
||||
tokenHandlers["{MediaInfo SubtitleLanguagesAll}"] = m => mediaInfoSubtitleLanguages;
|
||||
|
||||
tokenHandlers["{MediaInfo 3D}"] = m => mediaInfo3D;
|
||||
|
||||
tokenHandlers["{MediaInfo Simple}"] = m => $"{videoCodec} {audioCodec}";
|
||||
tokenHandlers["{MediaInfo Full}"] = m => $"{videoCodec} {audioCodec}{GetLanguagesToken(audioLanguages, m.CustomFormat, true, true)} {GetLanguagesToken(subtitles, m.CustomFormat, false, true)}";
|
||||
tokenHandlers["{MediaInfo Full}"] = m => $"{videoCodec} {audioCodec}{mediaInfoAudioLanguages} {mediaInfoSubtitleLanguages}";
|
||||
|
||||
tokenHandlers[MediaInfoVideoDynamicRangeToken] =
|
||||
m => MediaInfoFormatter.FormatVideoDynamicRange(movieFile.MediaInfo);
|
||||
@@ -401,7 +419,7 @@ namespace NzbDrone.Core.Organizer
|
||||
tokenHandlers["{Custom Formats}"] = m => string.Join(" ", customFormats.Where(x => x.IncludeCustomFormatWhenRenaming));
|
||||
}
|
||||
|
||||
private string GetLanguagesToken(List<string> mediaInfoLanguages, string filter, bool skipEnglishOnly, bool quoted)
|
||||
private string GetLanguagesToken(List<string> mediaInfoLanguages)
|
||||
{
|
||||
var tokens = new List<string>();
|
||||
foreach (var item in mediaInfoLanguages)
|
||||
@@ -430,44 +448,7 @@ namespace NzbDrone.Core.Organizer
|
||||
}
|
||||
}
|
||||
|
||||
tokens = tokens.Distinct().ToList();
|
||||
|
||||
var filteredTokens = tokens;
|
||||
|
||||
// Exclude or filter
|
||||
if (filter.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
if (filter.StartsWith("-"))
|
||||
{
|
||||
filteredTokens = tokens.Except(filter.Split('-')).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredTokens = filter.Split('+').Intersect(tokens).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
// Replace with wildcard (maybe too limited)
|
||||
if (filter.IsNotNullOrWhiteSpace() && filter.EndsWith("+") && filteredTokens.Count != tokens.Count)
|
||||
{
|
||||
filteredTokens.Add("--");
|
||||
}
|
||||
|
||||
if (skipEnglishOnly && filteredTokens.Count == 1 && filteredTokens.First() == "EN")
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var response = string.Join("+", filteredTokens);
|
||||
|
||||
if (quoted && response.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return $"[{response}]";
|
||||
}
|
||||
else
|
||||
{
|
||||
return response;
|
||||
}
|
||||
return string.Join("+", tokens.Distinct());
|
||||
}
|
||||
|
||||
private void UpdateMediaInfoIfNeeded(string pattern, MovieFile movieFile, Movie movie)
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Parser
|
||||
(?<polish>\b(?:PL\W?DUB|DUB\W?PL|LEK\W?PL|PL\W?LEK)\b)|
|
||||
(?<chinese>\[(?:CH[ST]|BIG5|GB)\]|简|繁|字幕)|
|
||||
(?<ukrainian>(?:(?:\dx)?UKR))|
|
||||
(?<spanish>\b(?:español|castellano)\b)|
|
||||
(?<latvian>\bLV\b)",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
@@ -56,7 +55,7 @@ namespace NzbDrone.Core.Parser
|
||||
languages.Add(Language.French);
|
||||
}
|
||||
|
||||
if (lowerTitle.Contains("spanish"))
|
||||
if (lowerTitle.Contains("spanish") || lowerTitle.Contains("castellano"))
|
||||
{
|
||||
languages.Add(Language.Spanish);
|
||||
}
|
||||
@@ -298,11 +297,6 @@ namespace NzbDrone.Core.Parser
|
||||
languages.Add(Language.Chinese);
|
||||
}
|
||||
|
||||
if (match.Groups["spanish"].Success)
|
||||
{
|
||||
languages.Add(Language.Spanish);
|
||||
}
|
||||
|
||||
if (match.Groups["ukrainian"].Success)
|
||||
{
|
||||
languages.Add(Language.Ukrainian);
|
||||
|
||||
@@ -25,8 +25,6 @@ namespace NzbDrone.Core.Parser.Model
|
||||
public bool SceneSource { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
public string Edition { get; set; }
|
||||
public string SceneName { get; set; }
|
||||
public bool OtherVideoFiles { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user