mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-15 21:05:48 -04:00
Compare commits
83 Commits
postgres-t
...
sonarr-pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27316b4239 | ||
|
|
9b3770a018 | ||
|
|
9db6289693 | ||
|
|
8a63f6ae37 | ||
|
|
069b18e5e3 | ||
|
|
f05333db51 | ||
|
|
f50e8f631e | ||
|
|
b9886cd11c | ||
|
|
9f3eecb2a9 | ||
|
|
52c24a4333 | ||
|
|
9bc31b46fa | ||
|
|
f4d8e113c1 | ||
|
|
1e1a4240d1 | ||
|
|
8fb53df4af | ||
|
|
f6dd600d2b | ||
|
|
40a15d59e0 | ||
|
|
c7baa66de2 | ||
|
|
2be70f5001 | ||
|
|
da857701f6 | ||
|
|
828d7eb1f3 | ||
|
|
b3a056edf9 | ||
|
|
98437c3cac | ||
|
|
c5616c5ba1 | ||
|
|
61979bff7a | ||
|
|
90d0d8bec8 | ||
|
|
2d814ecd20 | ||
|
|
6542119402 | ||
|
|
b9185574f3 | ||
|
|
99e0d42b71 | ||
|
|
d01fa5e6a4 | ||
|
|
2ce9d099e1 | ||
|
|
12829580e5 | ||
|
|
dadd796737 | ||
|
|
a3f508b8d4 | ||
|
|
1ab3df03a3 | ||
|
|
5558e10711 | ||
|
|
573405bae7 | ||
|
|
43d77308f9 | ||
|
|
b3c3f7ddae | ||
|
|
dd5bc41eda | ||
|
|
c8ab4f8c68 | ||
|
|
a4ddae0ccc | ||
|
|
d730161800 | ||
|
|
66c1af0555 | ||
|
|
dca00db317 | ||
|
|
812e5ac5a3 | ||
|
|
d01bae92bf | ||
|
|
1a6bf51741 | ||
|
|
f3e7843150 | ||
|
|
886b9b1c05 | ||
|
|
d8891ee4ea | ||
|
|
192dd9c137 | ||
|
|
b549fddf95 | ||
|
|
c1f538ed97 | ||
|
|
e72f8097fb | ||
|
|
3eec088306 | ||
|
|
ad097dd1a2 | ||
|
|
b4b38a5318 | ||
|
|
b0717a0803 | ||
|
|
4d1d08d345 | ||
|
|
e689817508 | ||
|
|
3b191caf16 | ||
|
|
cc6ca0b067 | ||
|
|
57cb63fb18 | ||
|
|
20f709d22a | ||
|
|
5d8775ac96 | ||
|
|
4890972e16 | ||
|
|
40dc808f61 | ||
|
|
97077e09d2 | ||
|
|
9ba7027d00 | ||
|
|
9903e70925 | ||
|
|
3a6f3666f5 | ||
|
|
915c66be50 | ||
|
|
70b22e483a | ||
|
|
cad1191da5 | ||
|
|
43910af127 | ||
|
|
f01c477b81 | ||
|
|
0054318658 | ||
|
|
03a3f4522a | ||
|
|
3d3562dcda | ||
|
|
7a079c5e0c | ||
|
|
4d70798f2f | ||
|
|
d55864f869 |
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"paths": [
|
"paths": [
|
||||||
"frontend/src/**/*.js",
|
"frontend/src/**/*.js"
|
||||||
"src/NzbDrone.Core/Localization/Core/*.json"
|
|
||||||
],
|
],
|
||||||
"ignored": [
|
"ignored": [
|
||||||
"**/node_modules/**/*"
|
"**/node_modules/**/*"
|
||||||
|
|||||||
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -5,9 +5,9 @@ body:
|
|||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Is there an existing issue for this?
|
label: Is there an existing issue for this?
|
||||||
description: Please search to see if an issue already exists for the bug you encountered.
|
description: Please search to see if an open or closed issue already exists for the bug you encountered. If a bug exists and is closed note that it may only be fixed in an unstable branch.
|
||||||
options:
|
options:
|
||||||
- label: I have searched the existing issues
|
- label: I have searched the existing open and closed issues
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
@@ -42,12 +42,14 @@ body:
|
|||||||
- **Docker Install**: Yes
|
- **Docker Install**: Yes
|
||||||
- **Using Reverse Proxy**: No
|
- **Using Reverse Proxy**: No
|
||||||
- **Browser**: Firefox 90 (If UI related)
|
- **Browser**: Firefox 90 (If UI related)
|
||||||
|
- **Database**: Sqlite 3.36.0
|
||||||
value: |
|
value: |
|
||||||
- OS:
|
- OS:
|
||||||
- Radarr:
|
- Radarr:
|
||||||
- Docker Install:
|
- Docker Install:
|
||||||
- Using Reverse Proxy:
|
- Using Reverse Proxy:
|
||||||
- Browser:
|
- Browser:
|
||||||
|
- Database:
|
||||||
render: markdown
|
render: markdown
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ variables:
|
|||||||
testsFolder: './_tests'
|
testsFolder: './_tests'
|
||||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||||
majorVersion: '4.2.0'
|
majorVersion: '4.2.4'
|
||||||
minorVersion: $[counter('minorVersion', 2000)]
|
minorVersion: $[counter('minorVersion', 2000)]
|
||||||
radarrVersion: '$(majorVersion).$(minorVersion)'
|
radarrVersion: '$(majorVersion).$(minorVersion)'
|
||||||
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
||||||
sentryOrg: 'servarr'
|
sentryOrg: 'servarr'
|
||||||
sentryUrl: 'https://sentry.servarr.com'
|
sentryUrl: 'https://sentry.servarr.com'
|
||||||
dotnetVersion: '6.0.300'
|
dotnetVersion: '6.0.400'
|
||||||
nodeVersion: '16.X'
|
nodeVersion: '16.X'
|
||||||
innoVersion: '6.2.0'
|
innoVersion: '6.2.0'
|
||||||
windowsImage: 'windows-2022'
|
windowsImage: 'windows-2022'
|
||||||
@@ -549,7 +549,7 @@ stages:
|
|||||||
Radarr__Postgres__Password: 'radarr'
|
Radarr__Postgres__Password: 'radarr'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-18.04'
|
vmImage: ${{ variables.linuxImage }}
|
||||||
|
|
||||||
timeoutInMinutes: 10
|
timeoutInMinutes: 10
|
||||||
|
|
||||||
@@ -687,7 +687,7 @@ stages:
|
|||||||
Radarr__Postgres__Password: 'radarr'
|
Radarr__Postgres__Password: 'radarr'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-18.04'
|
vmImage: ${{ variables.linuxImage }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
|
|||||||
@@ -223,7 +223,6 @@ module.exports = (env) => {
|
|||||||
{
|
{
|
||||||
loader: 'url-loader',
|
loader: 'url-loader',
|
||||||
options: {
|
options: {
|
||||||
limit: 24096,
|
|
||||||
mimetype: 'application/font-woff',
|
mimetype: 'application/font-woff',
|
||||||
emitFile: false,
|
emitFile: false,
|
||||||
name: 'Content/Fonts/[name].[ext]'
|
name: 'Content/Fonts/[name].[ext]'
|
||||||
@@ -233,12 +232,11 @@ module.exports = (env) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
test: /\.(ttf|eot|eot?#iefix|gif|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
test: /\.(ttf|eot|eot?#iefix|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
limit: 24096,
|
|
||||||
emitFile: false,
|
emitFile: false,
|
||||||
name: 'Content/Fonts/[name].[ext]'
|
name: 'Content/Fonts/[name].[ext]'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ class EditCollectionModalContent extends Component {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('Folder')}</FormLabel>
|
<FormLabel>{translate('RootFolder')}</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.ROOT_FOLDER_SELECT}
|
type={inputTypes.ROOT_FOLDER_SELECT}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Slider from 'react-slick';
|
|
||||||
import TextTruncate from 'react-text-truncate';
|
import TextTruncate from 'react-text-truncate';
|
||||||
|
import { Navigation } from 'swiper';
|
||||||
|
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||||
import EditCollectionModalConnector from 'Collection/Edit/EditCollectionModalConnector';
|
import EditCollectionModalConnector from 'Collection/Edit/EditCollectionModalConnector';
|
||||||
import CheckInput from 'Components/Form/CheckInput';
|
import CheckInput from 'Components/Form/CheckInput';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
@@ -17,8 +18,9 @@ import CollectionMovieConnector from './CollectionMovieConnector';
|
|||||||
import CollectionMovieLabelConnector from './CollectionMovieLabelConnector';
|
import CollectionMovieLabelConnector from './CollectionMovieLabelConnector';
|
||||||
import styles from './CollectionOverview.css';
|
import styles from './CollectionOverview.css';
|
||||||
|
|
||||||
import 'slick-carousel/slick/slick.css';
|
// Import Swiper styles
|
||||||
import 'slick-carousel/slick/slick-theme.css';
|
import 'swiper/css';
|
||||||
|
import 'swiper/css/navigation';
|
||||||
|
|
||||||
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
|
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
|
||||||
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
|
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
|
||||||
@@ -52,8 +54,12 @@ class CollectionOverview extends Component {
|
|||||||
//
|
//
|
||||||
// Control
|
// Control
|
||||||
|
|
||||||
setSliderRef = (ref) => {
|
setSliderPrevRef = (ref) => {
|
||||||
this.setState({ slider: ref });
|
this._swiperPrevRef = ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
setSliderNextRef = (ref) => {
|
||||||
|
this._swiperNextRef = ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -120,15 +126,6 @@ class CollectionOverview extends Component {
|
|||||||
const contentHeight = getContentHeight(rowHeight, isSmallScreen);
|
const contentHeight = getContentHeight(rowHeight, isSmallScreen);
|
||||||
const overviewHeight = contentHeight - titleRowHeight - posterHeight;
|
const overviewHeight = contentHeight - titleRowHeight - posterHeight;
|
||||||
|
|
||||||
const sliderSettings = {
|
|
||||||
arrows: false,
|
|
||||||
dots: false,
|
|
||||||
infinite: false,
|
|
||||||
slidesToShow: 1,
|
|
||||||
slidesToScroll: 1,
|
|
||||||
variableWidth: true
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
@@ -166,19 +163,21 @@ class CollectionOverview extends Component {
|
|||||||
{
|
{
|
||||||
showPosters &&
|
showPosters &&
|
||||||
<div className={styles.navigationButtons}>
|
<div className={styles.navigationButtons}>
|
||||||
<IconButton
|
<span ref={this.setSliderPrevRef}>
|
||||||
name={icons.ARROW_LEFT}
|
<IconButton
|
||||||
title={translate('ScrollMovies')}
|
name={icons.ARROW_LEFT}
|
||||||
onPress={this.state.slider?.slickPrev}
|
title={translate('ScrollMovies')}
|
||||||
size={20}
|
size={20}
|
||||||
/>
|
/>
|
||||||
|
</span>
|
||||||
|
|
||||||
<IconButton
|
<span ref={this.setSliderNextRef}>
|
||||||
name={icons.ARROW_RIGHT}
|
<IconButton
|
||||||
title={translate('ScrollMovies')}
|
name={icons.ARROW_RIGHT}
|
||||||
onPress={this.state.slider?.slickNext}
|
title={translate('ScrollMovies')}
|
||||||
size={20}
|
size={20}
|
||||||
/>
|
/>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,9 +269,23 @@ class CollectionOverview extends Component {
|
|||||||
{
|
{
|
||||||
showPosters ?
|
showPosters ?
|
||||||
<div className={styles.sliderContainer}>
|
<div className={styles.sliderContainer}>
|
||||||
<Slider ref={this.setSliderRef} {...sliderSettings}>
|
<Swiper
|
||||||
|
slidesPerView='auto'
|
||||||
|
spaceBetween={10}
|
||||||
|
slidesPerGroup={3}
|
||||||
|
loop={false}
|
||||||
|
loopFillGroupWithBlank={true}
|
||||||
|
className="mySwiper"
|
||||||
|
modules={[Navigation]}
|
||||||
|
onInit={(swiper) => {
|
||||||
|
swiper.params.navigation.prevEl = this._swiperPrevRef;
|
||||||
|
swiper.params.navigation.nextEl = this._swiperNextRef;
|
||||||
|
swiper.navigation.init();
|
||||||
|
swiper.navigation.update();
|
||||||
|
}}
|
||||||
|
>
|
||||||
{movies.map((movie) => (
|
{movies.map((movie) => (
|
||||||
<div className={styles.movie} key={movie.tmdbId}>
|
<SwiperSlide key={movie.tmdbId} style={{ width: posterWidth }}>
|
||||||
<CollectionMovieConnector
|
<CollectionMovieConnector
|
||||||
key={movie.tmdbId}
|
key={movie.tmdbId}
|
||||||
posterWidth={posterWidth}
|
posterWidth={posterWidth}
|
||||||
@@ -281,9 +294,9 @@ class CollectionOverview extends Component {
|
|||||||
collectionId={id}
|
collectionId={id}
|
||||||
{...movie}
|
{...movie}
|
||||||
/>
|
/>
|
||||||
</div>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
</Slider>
|
</Swiper>
|
||||||
</div> :
|
</div> :
|
||||||
<div className={styles.labelsContainer}>
|
<div className={styles.labelsContainer}>
|
||||||
{movies.map((movie) => (
|
{movies.map((movie) => (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
.jumpBar {
|
.jumpBar {
|
||||||
|
z-index: $pageJumpBarZIndex;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-content: stretch;
|
align-content: stretch;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
composes: link from '~Components/Link/Link.css';
|
composes: link from '~Components/Link/Link.css';
|
||||||
|
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
width: $toolbarButtonWidth;
|
min-width: $toolbarButtonWidth;
|
||||||
|
width: min-content;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ function HostSettings(props) {
|
|||||||
port,
|
port,
|
||||||
urlBase,
|
urlBase,
|
||||||
instanceName,
|
instanceName,
|
||||||
|
applicationUrl,
|
||||||
enableSsl,
|
enableSsl,
|
||||||
sslPort,
|
sslPort,
|
||||||
sslCertPath,
|
sslCertPath,
|
||||||
@@ -90,6 +91,21 @@ function HostSettings(props) {
|
|||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup
|
||||||
|
advancedSettings={advancedSettings}
|
||||||
|
isAdvanced={true}
|
||||||
|
>
|
||||||
|
<FormLabel>{translate('ApplicationURL')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.TEXT}
|
||||||
|
name="applicationUrl"
|
||||||
|
helpText={translate('ApplicationUrlHelpText')}
|
||||||
|
onChange={onInputChange}
|
||||||
|
{...applicationUrl}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
advancedSettings={advancedSettings}
|
advancedSettings={advancedSettings}
|
||||||
isAdvanced={true}
|
isAdvanced={true}
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ function EditImportListModalContent(props) {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('Folder')}</FormLabel>
|
<FormLabel>{translate('RootFolder')}</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.ROOT_FOLDER_SELECT}
|
type={inputTypes.ROOT_FOLDER_SELECT}
|
||||||
|
|||||||
@@ -262,10 +262,10 @@ export const defaultState = {
|
|||||||
type: filterBuilderTypes.ARRAY,
|
type: filterBuilderTypes.ARRAY,
|
||||||
optionsSelector: function(items) {
|
optionsSelector: function(items) {
|
||||||
const collectionList = items.reduce((acc, movie) => {
|
const collectionList = items.reduce((acc, movie) => {
|
||||||
if (movie.collection) {
|
if (movie.collection && movie.collection.title) {
|
||||||
acc.push({
|
acc.push({
|
||||||
id: movie.collection.name,
|
id: movie.collection.title,
|
||||||
name: movie.collection.name
|
name: movie.collection.title
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export const filterPredicates = {
|
|||||||
const predicate = filterTypePredicates[type];
|
const predicate = filterTypePredicates[type];
|
||||||
const { collection } = item;
|
const { collection } = item;
|
||||||
|
|
||||||
return predicate(collection ? collection.name : '', filterValue);
|
return predicate(collection && collection.title ? collection.title : '', filterValue);
|
||||||
},
|
},
|
||||||
|
|
||||||
originalLanguage: function(item, filterValue, type) {
|
originalLanguage: function(item, filterValue, type) {
|
||||||
|
|||||||
@@ -165,11 +165,11 @@ export const actionHandlers = handleThunks({
|
|||||||
requestData.quality = quality;
|
requestData.quality = quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (releaseGroup) {
|
if (releaseGroup !== undefined) {
|
||||||
requestData.releaseGroup = releaseGroup;
|
requestData.releaseGroup = releaseGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edition) {
|
if (edition !== undefined) {
|
||||||
requestData.edition = edition;
|
requestData.edition = edition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,11 +201,11 @@ export const actionHandlers = handleThunks({
|
|||||||
props.quality = quality;
|
props.quality = quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edition) {
|
if (edition !== undefined) {
|
||||||
props.edition = edition;
|
props.edition = edition;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (releaseGroup) {
|
if (releaseGroup !== undefined) {
|
||||||
props.releaseGroup = releaseGroup;
|
props.releaseGroup = releaseGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ export const defaultState = {
|
|||||||
collection: function(item) {
|
collection: function(item) {
|
||||||
const { collection ={} } = item;
|
const { collection ={} } = item;
|
||||||
|
|
||||||
return collection.name;
|
return collection.title;
|
||||||
},
|
},
|
||||||
|
|
||||||
originalLanguage: function(item) {
|
originalLanguage: function(item) {
|
||||||
@@ -339,10 +339,10 @@ export const defaultState = {
|
|||||||
type: filterBuilderTypes.ARRAY,
|
type: filterBuilderTypes.ARRAY,
|
||||||
optionsSelector: function(items) {
|
optionsSelector: function(items) {
|
||||||
const collectionList = items.reduce((acc, movie) => {
|
const collectionList = items.reduce((acc, movie) => {
|
||||||
if (movie.collection) {
|
if (movie.collection && movie.collection.title) {
|
||||||
acc.push({
|
acc.push({
|
||||||
id: movie.collection.name,
|
id: movie.collection.title,
|
||||||
name: movie.collection.name
|
name: movie.collection.title
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
pageJumpBarZIndex: 10,
|
||||||
modalZIndex: 1000,
|
modalZIndex: 1000,
|
||||||
popperZIndex: 2000
|
popperZIndex: 2000
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "6.1.0",
|
"@fortawesome/free-regular-svg-icons": "6.1.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "6.1.0",
|
"@fortawesome/free-solid-svg-icons": "6.1.0",
|
||||||
"@fortawesome/react-fontawesome": "0.1.18",
|
"@fortawesome/react-fontawesome": "0.1.18",
|
||||||
"@microsoft/signalr": "6.0.5",
|
"@microsoft/signalr": "6.0.8",
|
||||||
"@sentry/browser": "6.18.2",
|
"@sentry/browser": "6.18.2",
|
||||||
"@sentry/integrations": "6.18.2",
|
"@sentry/integrations": "6.18.2",
|
||||||
"classnames": "2.3.1",
|
"classnames": "2.3.1",
|
||||||
@@ -62,8 +62,7 @@
|
|||||||
"react-document-title": "2.0.3",
|
"react-document-title": "2.0.3",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-focus-lock": "2.5.0",
|
"react-focus-lock": "2.5.0",
|
||||||
"react-slick": "0.28.1",
|
"swiper": "8.3.2",
|
||||||
"slick-carousel": "1.8.1",
|
|
||||||
"react-google-recaptcha": "2.1.0",
|
"react-google-recaptcha": "2.1.0",
|
||||||
"react-lazyload": "3.2.0",
|
"react-lazyload": "3.2.0",
|
||||||
"react-measure": "1.4.7",
|
"react-measure": "1.4.7",
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
|
|
||||||
<!-- Standard testing packages -->
|
<!-- Standard testing packages -->
|
||||||
<ItemGroup Condition="'$(TestProject)'=='true'">
|
<ItemGroup Condition="'$(TestProject)'=='true'">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
<PackageReference Include="NunitXml.TestLogger" Version="3.0.117" />
|
<PackageReference Include="NunitXml.TestLogger" Version="3.0.117" />
|
||||||
|
|||||||
@@ -8,5 +8,6 @@
|
|||||||
<add key="SQLite" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/SQLite/nuget/v3/index.json" />
|
<add key="SQLite" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/SQLite/nuget/v3/index.json" />
|
||||||
<add key="coverlet-nightly" value="https://pkgs.dev.azure.com/Servarr/coverlet/_packaging/coverlet-nightly/nuget/v3/index.json" />
|
<add key="coverlet-nightly" value="https://pkgs.dev.azure.com/Servarr/coverlet/_packaging/coverlet-nightly/nuget/v3/index.json" />
|
||||||
<add key="FFMpegCore" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/FFMpegCore/nuget/v3/index.json" />
|
<add key="FFMpegCore" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/FFMpegCore/nuget/v3/index.json" />
|
||||||
|
<add key="FluentMigrator" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/FluentMigrator/nuget/v3/index.json" />
|
||||||
</packageSources>
|
</packageSources>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
|||||||
[TestCase("https://notifiarr.com/notifier.php: api=1234530f-422f-4aac-b6b3-01233210aaaa&radarr_health_issue_message=Download")]
|
[TestCase("https://notifiarr.com/notifier.php: api=1234530f-422f-4aac-b6b3-01233210aaaa&radarr_health_issue_message=Download")]
|
||||||
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
|
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
|
||||||
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;Enlist=False ***")]
|
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;Enlist=False ***")]
|
||||||
|
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
|
||||||
|
|
||||||
// Announce URLs (passkeys) Magnet & Tracker
|
// Announce URLs (passkeys) Magnet & Tracker
|
||||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
|
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
|
||||||
@@ -84,9 +85,24 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
|||||||
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
||||||
|
|
||||||
cleansedMessage.Should().NotContain("mySecret");
|
cleansedMessage.Should().NotContain("mySecret");
|
||||||
|
cleansedMessage.Should().NotContain("123%@%_@!#^#@");
|
||||||
cleansedMessage.Should().NotContain("01233210");
|
cleansedMessage.Should().NotContain("01233210");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
|
||||||
|
public void should_keep_message(string message)
|
||||||
|
{
|
||||||
|
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
||||||
|
|
||||||
|
cleansedMessage.Should().NotContain("mySecret");
|
||||||
|
cleansedMessage.Should().NotContain("123%@%_@!#^#@");
|
||||||
|
cleansedMessage.Should().NotContain("01233210");
|
||||||
|
|
||||||
|
cleansedMessage.Should().Contain("shouldkeep1");
|
||||||
|
cleansedMessage.Should().Contain("shouldkeep2");
|
||||||
|
cleansedMessage.Should().Contain("shouldkeep3");
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase(@"Some message (from 32.2.3.5 user agent)")]
|
[TestCase(@"Some message (from 32.2.3.5 user agent)")]
|
||||||
[TestCase(@"Auth-Invalidated ip 32.2.3.5")]
|
[TestCase(@"Auth-Invalidated ip 32.2.3.5")]
|
||||||
[TestCase(@"Auth-Success ip 32.2.3.5")]
|
[TestCase(@"Auth-Success ip 32.2.3.5")]
|
||||||
|
|||||||
@@ -18,9 +18,26 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
|||||||
private static LogLevel[] SentryLevels = LogLevel.AllLevels.Where(x => x >= LogLevel.Error).ToArray();
|
private static LogLevel[] SentryLevels = LogLevel.AllLevels.Where(x => x >= LogLevel.Error).ToArray();
|
||||||
private static LogLevel[] OtherLevels = AllLevels.Except(SentryLevels).ToArray();
|
private static LogLevel[] OtherLevels = AllLevels.Except(SentryLevels).ToArray();
|
||||||
|
|
||||||
|
// TODO: SQLiteException filtering tests don't work on linux-86 and alpine customer Azure agents due to sqlite library not being loaded up, pass local
|
||||||
private static Exception[] FilteredExceptions = new Exception[]
|
private static Exception[] FilteredExceptions = new Exception[]
|
||||||
{
|
{
|
||||||
new UnauthorizedAccessException()
|
// new SQLiteException(SQLiteErrorCode.Locked, "database is locked"),
|
||||||
|
new UnauthorizedAccessException(),
|
||||||
|
new AggregateException(new Exception[]
|
||||||
|
{
|
||||||
|
new UnauthorizedAccessException(),
|
||||||
|
new UnauthorizedAccessException()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Exception[] NonFilteredExceptions = new Exception[]
|
||||||
|
{
|
||||||
|
// new SQLiteException(SQLiteErrorCode.Error, "it's borked"),
|
||||||
|
new AggregateException(new Exception[]
|
||||||
|
{
|
||||||
|
new UnauthorizedAccessException(),
|
||||||
|
new NotImplementedException()
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
@@ -63,6 +80,14 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
|||||||
_subject.IsSentryMessage(log).Should().BeFalse();
|
_subject.IsSentryMessage(log).Should().BeFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCaseSource("NonFilteredExceptions")]
|
||||||
|
public void should_not_filter_event_for_filtered_exception_types(Exception ex)
|
||||||
|
{
|
||||||
|
var log = GivenLogEvent(LogLevel.Error, ex, "test");
|
||||||
|
_subject.IsSentryMessage(log).Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[TestCaseSource("FilteredExceptions")]
|
[TestCaseSource("FilteredExceptions")]
|
||||||
public void should_not_filter_event_for_filtered_exception_types_if_filtering_disabled(Exception ex)
|
public void should_not_filter_event_for_filtered_exception_types_if_filtering_disabled(Exception ex)
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ namespace NzbDrone.Common.Http
|
|||||||
StatusCode == HttpStatusCode.Found ||
|
StatusCode == HttpStatusCode.Found ||
|
||||||
StatusCode == HttpStatusCode.TemporaryRedirect ||
|
StatusCode == HttpStatusCode.TemporaryRedirect ||
|
||||||
StatusCode == HttpStatusCode.RedirectMethod ||
|
StatusCode == HttpStatusCode.RedirectMethod ||
|
||||||
StatusCode == HttpStatusCode.SeeOther;
|
StatusCode == HttpStatusCode.SeeOther ||
|
||||||
|
StatusCode == HttpStatusCode.PermanentRedirect;
|
||||||
|
|
||||||
public string[] GetCookieHeaders()
|
public string[] GetCookieHeaders()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace NzbDrone.Common.Instrumentation
|
|||||||
new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||||
new Regex(@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
|
new Regex(@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
|
||||||
new Regex(@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
new Regex(@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||||
new Regex(@"(?<=[?& ;])[^=]*?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
new Regex(@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||||
|
|
||||||
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
|
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
|
||||||
new Regex(@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"),
|
new Regex(@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"),
|
||||||
|
|||||||
@@ -229,21 +229,48 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
|||||||
{
|
{
|
||||||
if (FilterEvents)
|
if (FilterEvents)
|
||||||
{
|
{
|
||||||
var sqlEx = logEvent.Exception as SQLiteException;
|
var exceptions = new List<Exception>();
|
||||||
if (sqlEx != null && FilteredSQLiteErrors.Contains(sqlEx.ResultCode))
|
|
||||||
|
var aggEx = logEvent.Exception as AggregateException;
|
||||||
|
|
||||||
|
if (aggEx != null && aggEx.InnerExceptions.Count > 0)
|
||||||
{
|
{
|
||||||
return false;
|
exceptions.AddRange(aggEx.InnerExceptions);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
exceptions.Add(logEvent.Exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FilteredExceptionTypeNames.Contains(logEvent.Exception.GetType().Name))
|
// If any are sentry then send to sentry
|
||||||
|
foreach (var ex in exceptions)
|
||||||
{
|
{
|
||||||
return false;
|
var isSentry = true;
|
||||||
|
|
||||||
|
var sqlEx = ex as SQLiteException;
|
||||||
|
if (sqlEx != null && FilteredSQLiteErrors.Contains(sqlEx.ResultCode))
|
||||||
|
{
|
||||||
|
isSentry = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FilteredExceptionTypeNames.Contains(ex.GetType().Name))
|
||||||
|
{
|
||||||
|
isSentry = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FilteredExceptionMessages.Any(x => ex.Message.Contains(x)))
|
||||||
|
{
|
||||||
|
isSentry = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSentry)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FilteredExceptionMessages.Any(x => logEvent.Exception.Message.Contains(x)))
|
// The exception or aggregate exception children were not sentry exceptions
|
||||||
{
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -10,10 +10,10 @@
|
|||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="NLog" Version="5.0.1" />
|
<PackageReference Include="NLog" Version="5.0.1" />
|
||||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
|
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.15.0" />
|
<PackageReference Include="Sentry" Version="3.20.1" />
|
||||||
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
|
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||||
<PackageReference Include="System.Text.Json" Version="6.0.4" />
|
<PackageReference Include="System.Text.Json" Version="6.0.5" />
|
||||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
|
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
|
||||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
|
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
|
||||||
|
|||||||
@@ -27,6 +27,20 @@ namespace NzbDrone.Core.Test.Datastore
|
|||||||
Mocker.Resolve<IDatabase>().Vacuum();
|
Mocker.Resolve<IDatabase>().Vacuum();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void postgres_should_not_contain_timestamp_without_timezone_columns()
|
||||||
|
{
|
||||||
|
if (Db.DatabaseType != DatabaseType.PostgreSQL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mocker.Resolve<IDatabase>()
|
||||||
|
.OpenConnection().Query("SELECT table_name, column_name, data_type FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = 'public' AND data_type = 'timestamp without time zone'")
|
||||||
|
.Should()
|
||||||
|
.BeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void get_version()
|
public void get_version()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using FizzWare.NBuilder;
|
using System;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Movies;
|
using NzbDrone.Core.Movies;
|
||||||
@@ -168,5 +170,80 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||||||
.Should()
|
.Should()
|
||||||
.BeFalse();
|
.BeFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_false_when_repack_but_auto_download_repack_is_false()
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IConfigService>()
|
||||||
|
.Setup(s => s.DownloadPropersAndRepacks)
|
||||||
|
.Returns(ProperDownloadTypes.DoNotUpgrade);
|
||||||
|
|
||||||
|
_parsedMovieInfo.Quality.Revision.IsRepack = true;
|
||||||
|
_movie.MovieFileId = 1;
|
||||||
|
_movie.MovieFile = Builder<MovieFile>.CreateNew()
|
||||||
|
.With(e => e.Quality = new QualityModel(Quality.SDTV))
|
||||||
|
.With(e => e.ReleaseGroup = "Radarr")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var remoteMovie = Builder<RemoteMovie>.CreateNew()
|
||||||
|
.With(e => e.ParsedMovieInfo = _parsedMovieInfo)
|
||||||
|
.With(e => e.Movie = _movie)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
Subject.IsSatisfiedBy(remoteMovie, null)
|
||||||
|
.Accepted
|
||||||
|
.Should()
|
||||||
|
.BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_true_when_repack_but_auto_download_repack_is_true()
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IConfigService>()
|
||||||
|
.Setup(s => s.DownloadPropersAndRepacks)
|
||||||
|
.Returns(ProperDownloadTypes.PreferAndUpgrade);
|
||||||
|
|
||||||
|
_parsedMovieInfo.Quality.Revision.IsRepack = true;
|
||||||
|
_movie.MovieFileId = 1;
|
||||||
|
_movie.MovieFile = Builder<MovieFile>.CreateNew()
|
||||||
|
.With(e => e.Quality = new QualityModel(Quality.SDTV))
|
||||||
|
.With(e => e.ReleaseGroup = "Radarr")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var remoteMovie = Builder<RemoteMovie>.CreateNew()
|
||||||
|
.With(e => e.ParsedMovieInfo = _parsedMovieInfo)
|
||||||
|
.With(e => e.Movie = _movie)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
Subject.IsSatisfiedBy(remoteMovie, null)
|
||||||
|
.Accepted
|
||||||
|
.Should()
|
||||||
|
.BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_true_when_repacks_are_not_preferred()
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IConfigService>()
|
||||||
|
.Setup(s => s.DownloadPropersAndRepacks)
|
||||||
|
.Returns(ProperDownloadTypes.DoNotPrefer);
|
||||||
|
|
||||||
|
_parsedMovieInfo.Quality.Revision.IsRepack = true;
|
||||||
|
_movie.MovieFileId = 1;
|
||||||
|
_movie.MovieFile = Builder<MovieFile>.CreateNew()
|
||||||
|
.With(e => e.Quality = new QualityModel(Quality.SDTV))
|
||||||
|
.With(e => e.ReleaseGroup = "Radarr")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var remoteMovie = Builder<RemoteMovie>.CreateNew()
|
||||||
|
.With(e => e.ParsedMovieInfo = _parsedMovieInfo)
|
||||||
|
.With(e => e.Movie = _movie)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
Subject.IsSatisfiedBy(remoteMovie, null)
|
||||||
|
.Accepted
|
||||||
|
.Should()
|
||||||
|
.BeTrue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
245
src/NzbDrone.Core.Test/Extras/ExtraServiceFixture.cs
Normal file
245
src/NzbDrone.Core.Test/Extras/ExtraServiceFixture.cs
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
|
using Moq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Extras;
|
||||||
|
using NzbDrone.Core.Extras.Files;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.Extras
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class ExtraServiceFixture : CoreTest<ExtraService>
|
||||||
|
{
|
||||||
|
private Movie _movie;
|
||||||
|
private MovieFile _movieFile;
|
||||||
|
private LocalMovie _localMovie;
|
||||||
|
|
||||||
|
private string _movieFolder;
|
||||||
|
private string _releaseFolder;
|
||||||
|
|
||||||
|
private Mock<IManageExtraFiles> _subtitleService;
|
||||||
|
private Mock<IManageExtraFiles> _otherExtraService;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_movieFolder = @"C:\Test\Movies\Movie Title".AsOsAgnostic();
|
||||||
|
_releaseFolder = @"C:\Test\Unsorted TV\Movie.Title.2022".AsOsAgnostic();
|
||||||
|
|
||||||
|
_movie = Builder<Movie>.CreateNew()
|
||||||
|
.With(s => s.Path = _movieFolder)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_movieFile = Builder<MovieFile>.CreateNew()
|
||||||
|
.With(f => f.Path = Path.Combine(_movie.Path, "Movie Title - 2022.mkv").AsOsAgnostic())
|
||||||
|
.With(f => f.RelativePath = @"Movie Title - 2022.mkv".AsOsAgnostic())
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_localMovie = Builder<LocalMovie>.CreateNew()
|
||||||
|
.With(l => l.Movie = _movie)
|
||||||
|
.With(l => l.Path = Path.Combine(_releaseFolder, "Movie.Title.2022.mkv").AsOsAgnostic())
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_subtitleService = new Mock<IManageExtraFiles>();
|
||||||
|
_subtitleService.SetupGet(s => s.Order).Returns(0);
|
||||||
|
_subtitleService.Setup(s => s.CanImportFile(It.IsAny<LocalMovie>(), It.IsAny<MovieFile>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
|
||||||
|
.Returns(false);
|
||||||
|
_subtitleService.Setup(s => s.CanImportFile(It.IsAny<LocalMovie>(), It.IsAny<MovieFile>(), It.IsAny<string>(), ".srt", It.IsAny<bool>()))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
_otherExtraService = new Mock<IManageExtraFiles>();
|
||||||
|
_otherExtraService.SetupGet(s => s.Order).Returns(1);
|
||||||
|
_otherExtraService.Setup(s => s.CanImportFile(It.IsAny<LocalMovie>(), It.IsAny<MovieFile>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
Mocker.SetConstant<IEnumerable<IManageExtraFiles>>(new[]
|
||||||
|
{
|
||||||
|
_subtitleService.Object,
|
||||||
|
_otherExtraService.Object
|
||||||
|
});
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>().Setup(s => s.FolderExists(It.IsAny<string>()))
|
||||||
|
.Returns(false);
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetParentFolder(It.IsAny<string>()))
|
||||||
|
.Returns((string path) => Directory.GetParent(path).FullName);
|
||||||
|
|
||||||
|
WithExistingFolder(_movie.Path);
|
||||||
|
WithExistingFile(_movieFile.Path);
|
||||||
|
WithExistingFile(_localMovie.Path);
|
||||||
|
|
||||||
|
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(true);
|
||||||
|
Mocker.GetMock<IConfigService>().Setup(v => v.ExtraFileExtensions).Returns("nfo,srt");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WithExistingFolder(string path, bool exists = true)
|
||||||
|
{
|
||||||
|
var dir = Path.GetDirectoryName(path);
|
||||||
|
|
||||||
|
if (exists && dir.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
WithExistingFolder(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>().Setup(v => v.FolderExists(path)).Returns(exists);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WithExistingFile(string path, bool exists = true, int size = 1000)
|
||||||
|
{
|
||||||
|
var dir = Path.GetDirectoryName(path);
|
||||||
|
|
||||||
|
if (exists && dir.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
WithExistingFolder(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>().Setup(v => v.FileExists(path)).Returns(exists);
|
||||||
|
Mocker.GetMock<IDiskProvider>().Setup(v => v.GetFileSize(path)).Returns(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WithExistingFiles(List<string> files)
|
||||||
|
{
|
||||||
|
foreach (string file in files)
|
||||||
|
{
|
||||||
|
WithExistingFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(_releaseFolder, It.IsAny<SearchOption>()))
|
||||||
|
.Returns(files.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_not_pass_file_if_import_disabled()
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(false);
|
||||||
|
|
||||||
|
var nfofile = Path.Combine(_releaseFolder, "Movie.Title.2022.nfo").AsOsAgnostic();
|
||||||
|
|
||||||
|
var files = new List<string>
|
||||||
|
{
|
||||||
|
_localMovie.Path,
|
||||||
|
nfofile
|
||||||
|
};
|
||||||
|
|
||||||
|
WithExistingFiles(files);
|
||||||
|
|
||||||
|
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||||
|
|
||||||
|
_subtitleService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
|
||||||
|
_otherExtraService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase("Movie Title - 2022.sub")]
|
||||||
|
[TestCase("Movie Title - 2022.ass")]
|
||||||
|
public void should_not_pass_unwanted_file(string filePath)
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(false);
|
||||||
|
|
||||||
|
var nfofile = Path.Combine(_releaseFolder, filePath).AsOsAgnostic();
|
||||||
|
|
||||||
|
var files = new List<string>
|
||||||
|
{
|
||||||
|
_localMovie.Path,
|
||||||
|
nfofile
|
||||||
|
};
|
||||||
|
|
||||||
|
WithExistingFiles(files);
|
||||||
|
|
||||||
|
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||||
|
|
||||||
|
_subtitleService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
|
||||||
|
_otherExtraService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_pass_subtitle_file_to_subtitle_service()
|
||||||
|
{
|
||||||
|
var subtitleFile = Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic();
|
||||||
|
|
||||||
|
var files = new List<string>
|
||||||
|
{
|
||||||
|
_localMovie.Path,
|
||||||
|
subtitleFile
|
||||||
|
};
|
||||||
|
|
||||||
|
WithExistingFiles(files);
|
||||||
|
|
||||||
|
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||||
|
|
||||||
|
_subtitleService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true), Times.Once());
|
||||||
|
_otherExtraService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true), Times.Never());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_pass_nfo_file_to_other_service()
|
||||||
|
{
|
||||||
|
var nfofile = Path.Combine(_releaseFolder, "Movie.Title.2022.nfo").AsOsAgnostic();
|
||||||
|
|
||||||
|
var files = new List<string>
|
||||||
|
{
|
||||||
|
_localMovie.Path,
|
||||||
|
nfofile
|
||||||
|
};
|
||||||
|
|
||||||
|
WithExistingFiles(files);
|
||||||
|
|
||||||
|
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||||
|
|
||||||
|
_subtitleService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { nfofile }, true), Times.Never());
|
||||||
|
_otherExtraService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { nfofile }, true), Times.Once());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_search_subtitles_when_importing_from_job_folder()
|
||||||
|
{
|
||||||
|
_localMovie.FolderMovieInfo = new ParsedMovieInfo();
|
||||||
|
|
||||||
|
var subtitleFile = Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic();
|
||||||
|
|
||||||
|
var files = new List<string>
|
||||||
|
{
|
||||||
|
_localMovie.Path,
|
||||||
|
subtitleFile
|
||||||
|
};
|
||||||
|
|
||||||
|
WithExistingFiles(files);
|
||||||
|
|
||||||
|
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.AllDirectories), Times.Once);
|
||||||
|
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.TopDirectoryOnly), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_not_search_subtitles_when_not_importing_from_job_folder()
|
||||||
|
{
|
||||||
|
_localMovie.FolderMovieInfo = null;
|
||||||
|
|
||||||
|
var subtitleFile = Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic();
|
||||||
|
|
||||||
|
var files = new List<string>
|
||||||
|
{
|
||||||
|
_localMovie.Path,
|
||||||
|
subtitleFile
|
||||||
|
};
|
||||||
|
|
||||||
|
WithExistingFiles(files);
|
||||||
|
|
||||||
|
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.AllDirectories), Times.Never);
|
||||||
|
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.TopDirectoryOnly), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
|
using FluentAssertions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Extras.Others;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.Extras.Others
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class OtherExtraServiceFixture : CoreTest<OtherExtraService>
|
||||||
|
{
|
||||||
|
private Movie _movie;
|
||||||
|
private MovieFile _movieFile;
|
||||||
|
private LocalMovie _localMovie;
|
||||||
|
|
||||||
|
private string _movieFolder;
|
||||||
|
private string _releaseFolder;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_movieFolder = @"C:\Test\Movies\Movie Title".AsOsAgnostic();
|
||||||
|
_releaseFolder = @"C:\Test\Unsorted Movies\Movie.Title.2022".AsOsAgnostic();
|
||||||
|
|
||||||
|
_movie = Builder<Movie>.CreateNew()
|
||||||
|
.With(s => s.Path = _movieFolder)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_movieFile = Builder<MovieFile>.CreateNew()
|
||||||
|
.With(f => f.Path = Path.Combine(_movie.Path, "Movie Title - 2022.mkv").AsOsAgnostic())
|
||||||
|
.With(f => f.RelativePath = @"Movie Title - 2022.mkv")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_localMovie = Builder<LocalMovie>.CreateNew()
|
||||||
|
.With(l => l.Movie = _movie)
|
||||||
|
.With(l => l.Path = Path.Combine(_releaseFolder, "Movie.Title.2022.mkv").AsOsAgnostic())
|
||||||
|
.With(l => l.FileMovieInfo = new ParsedMovieInfo
|
||||||
|
{
|
||||||
|
MovieTitles = new List<string> { "Movie Title" },
|
||||||
|
Year = 2022
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase("Movie Title - 2022.nfo", "Movie Title - 2022.nfo")]
|
||||||
|
[TestCase("Movie.Title.2022.nfo", "Movie Title - 2022.nfo")]
|
||||||
|
[TestCase("Movie Title 2022.nfo", "Movie Title - 2022.nfo")]
|
||||||
|
[TestCase("Movie_Title_2022.nfo", "Movie Title - 2022.nfo")]
|
||||||
|
[TestCase(@"Movie.Title.2022\thumb.jpg", "Movie Title - 2022.jpg")]
|
||||||
|
public void should_import_matching_file(string filePath, string expectedOutputPath)
|
||||||
|
{
|
||||||
|
var files = new List<string> { Path.Combine(_releaseFolder, filePath).AsOsAgnostic() };
|
||||||
|
|
||||||
|
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||||
|
|
||||||
|
results.Count().Should().Be(1);
|
||||||
|
|
||||||
|
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_not_import_multiple_nfo_files()
|
||||||
|
{
|
||||||
|
var files = new List<string>
|
||||||
|
{
|
||||||
|
Path.Combine(_releaseFolder, "Movie.Title.2022.nfo").AsOsAgnostic(),
|
||||||
|
Path.Combine(_releaseFolder, "Movie_Title_2022.nfo").AsOsAgnostic(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||||
|
|
||||||
|
results.Count().Should().Be(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Extras.Subtitles;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.MediaFiles.MovieImport;
|
||||||
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.Extras.Subtitles
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class SubtitleServiceFixture : CoreTest<SubtitleService>
|
||||||
|
{
|
||||||
|
private Movie _movie;
|
||||||
|
private MovieFile _movieFile;
|
||||||
|
private LocalMovie _localMovie;
|
||||||
|
|
||||||
|
private string _MovieFolder;
|
||||||
|
private string _releaseFolder;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_MovieFolder = @"C:\Test\Movies\Movie Title".AsOsAgnostic();
|
||||||
|
_releaseFolder = @"C:\Test\Unsorted Movies\Movie.Title.2022".AsOsAgnostic();
|
||||||
|
|
||||||
|
_movie = Builder<Movie>.CreateNew()
|
||||||
|
.With(s => s.Path = _MovieFolder)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_movieFile = Builder<MovieFile>.CreateNew()
|
||||||
|
.With(f => f.Path = Path.Combine(_movie.Path, "Movie Title - 2022.mkv").AsOsAgnostic())
|
||||||
|
.With(f => f.RelativePath = @"Movie Title - 2022.mkv".AsOsAgnostic())
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_localMovie = Builder<LocalMovie>.CreateNew()
|
||||||
|
.With(l => l.Movie = _movie)
|
||||||
|
.With(l => l.Path = Path.Combine(_releaseFolder, "Movie.Title.2022.mkv").AsOsAgnostic())
|
||||||
|
.With(l => l.FileMovieInfo = new ParsedMovieInfo
|
||||||
|
{
|
||||||
|
MovieTitles = new List<string> { "Movie Title" },
|
||||||
|
Year = 2022
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetParentFolder(It.IsAny<string>()))
|
||||||
|
.Returns((string path) => Directory.GetParent(path).FullName);
|
||||||
|
|
||||||
|
Mocker.GetMock<IDetectSample>().Setup(s => s.IsSample(It.IsAny<MovieMetadata>(), It.IsAny<string>()))
|
||||||
|
.Returns(DetectSampleResult.NotSample);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase("Movie.Title.2022.en.nfo")]
|
||||||
|
public void should_not_import_non_subtitle_file(string filePath)
|
||||||
|
{
|
||||||
|
var files = new List<string> { Path.Combine(_releaseFolder, filePath).AsOsAgnostic() };
|
||||||
|
|
||||||
|
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||||
|
|
||||||
|
results.Count().Should().Be(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase("Movie Title - 2022.srt", "Movie Title - 2022.srt")]
|
||||||
|
[TestCase("Movie.Title.2022.en.srt", "Movie Title - 2022.en.srt")]
|
||||||
|
[TestCase("Movie.Title.2022.english.srt", "Movie Title - 2022.en.srt")]
|
||||||
|
[TestCase("Movie Title 2022_en_sdh_forced.srt", "Movie Title - 2022.en.sdh.forced.srt")]
|
||||||
|
[TestCase("Movie_Title_2022 en.srt", "Movie Title - 2022.en.srt")]
|
||||||
|
[TestCase(@"Subs\Movie.Title.2022\2_en.srt", "Movie Title - 2022.en.srt")]
|
||||||
|
[TestCase("sub.srt", "Movie Title - 2022.srt")]
|
||||||
|
public void should_import_matching_subtitle_file(string filePath, string expectedOutputPath)
|
||||||
|
{
|
||||||
|
var files = new List<string> { Path.Combine(_releaseFolder, filePath).AsOsAgnostic() };
|
||||||
|
|
||||||
|
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||||
|
|
||||||
|
results.Count().Should().Be(1);
|
||||||
|
|
||||||
|
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_import_multiple_subtitle_files_per_language()
|
||||||
|
{
|
||||||
|
var files = new List<string>
|
||||||
|
{
|
||||||
|
Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic(),
|
||||||
|
Path.Combine(_releaseFolder, "Movie.Title.2022.eng.srt").AsOsAgnostic(),
|
||||||
|
Path.Combine(_releaseFolder, "Subs", "Movie_Title_2022_en_forced.srt").AsOsAgnostic(),
|
||||||
|
Path.Combine(_releaseFolder, "Subs", "Movie.Title.2022", "2_fr.srt").AsOsAgnostic()
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedOutputs = new string[]
|
||||||
|
{
|
||||||
|
"Movie Title - 2022.1.en.srt",
|
||||||
|
"Movie Title - 2022.2.en.srt",
|
||||||
|
"Movie Title - 2022.en.forced.srt",
|
||||||
|
"Movie Title - 2022.fr.srt",
|
||||||
|
};
|
||||||
|
|
||||||
|
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||||
|
|
||||||
|
results.Count().Should().Be(expectedOutputs.Length);
|
||||||
|
|
||||||
|
for (int i = 0; i < expectedOutputs.Length; i++)
|
||||||
|
{
|
||||||
|
results[i].RelativePath.AsOsAgnostic().PathEquals(expectedOutputs[i].AsOsAgnostic()).Should().Be(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_import_multiple_subtitle_files_per_language_with_tags()
|
||||||
|
{
|
||||||
|
var files = new List<string>
|
||||||
|
{
|
||||||
|
Path.Combine(_releaseFolder, "Movie.Title.2022.en.forced.cc.srt").AsOsAgnostic(),
|
||||||
|
Path.Combine(_releaseFolder, "Movie.Title.2022.other.en.forced.cc.srt").AsOsAgnostic(),
|
||||||
|
Path.Combine(_releaseFolder, "Movie.Title.2022.en.forced.sdh.srt").AsOsAgnostic(),
|
||||||
|
Path.Combine(_releaseFolder, "Movie.Title.2022.en.forced.default.srt").AsOsAgnostic(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedOutputs = new[]
|
||||||
|
{
|
||||||
|
"Movie Title - 2022.1.en.forced.cc.srt",
|
||||||
|
"Movie Title - 2022.2.en.forced.cc.srt",
|
||||||
|
"Movie Title - 2022.en.forced.sdh.srt",
|
||||||
|
"Movie Title - 2022.en.forced.default.srt"
|
||||||
|
};
|
||||||
|
|
||||||
|
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||||
|
|
||||||
|
results.Count().Should().Be(expectedOutputs.Length);
|
||||||
|
|
||||||
|
for (int i = 0; i < expectedOutputs.Length; i++)
|
||||||
|
{
|
||||||
|
results[i].RelativePath.AsOsAgnostic().PathEquals(expectedOutputs[i].AsOsAgnostic()).Should().Be(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase(@"Subs\2_en.srt", "Movie Title - 2022.en.srt")]
|
||||||
|
public void should_import_unmatching_subtitle_file_if_only_episode(string filePath, string expectedOutputPath)
|
||||||
|
{
|
||||||
|
var subtitleFile = Path.Combine(_releaseFolder, filePath).AsOsAgnostic();
|
||||||
|
|
||||||
|
var sampleFile = Path.Combine(_movie.Path, "Movie Title - 2022.sample.mkv").AsOsAgnostic();
|
||||||
|
|
||||||
|
var videoFiles = new string[]
|
||||||
|
{
|
||||||
|
_localMovie.Path,
|
||||||
|
sampleFile
|
||||||
|
};
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
|
||||||
|
.Returns(videoFiles);
|
||||||
|
|
||||||
|
Mocker.GetMock<IDetectSample>().Setup(s => s.IsSample(It.IsAny<MovieMetadata>(), sampleFile))
|
||||||
|
.Returns(DetectSampleResult.Sample);
|
||||||
|
|
||||||
|
var results = Subject.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true).ToList();
|
||||||
|
|
||||||
|
results.Count().Should().Be(1);
|
||||||
|
|
||||||
|
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
|
||||||
|
|
||||||
|
ExceptionVerification.ExpectedWarns(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,6 +47,8 @@ namespace NzbDrone.Core.Test.Languages
|
|||||||
new object[] { 32, Language.Ukrainian },
|
new object[] { 32, Language.Ukrainian },
|
||||||
new object[] { 33, Language.Persian },
|
new object[] { 33, Language.Persian },
|
||||||
new object[] { 34, Language.Bengali },
|
new object[] { 34, Language.Bengali },
|
||||||
|
new object[] { 35, Language.Slovak },
|
||||||
|
new object[] { 36, Language.Latvian },
|
||||||
};
|
};
|
||||||
|
|
||||||
public static object[] ToIntCases =
|
public static object[] ToIntCases =
|
||||||
@@ -88,6 +90,8 @@ namespace NzbDrone.Core.Test.Languages
|
|||||||
new object[] { Language.Ukrainian, 32 },
|
new object[] { Language.Ukrainian, 32 },
|
||||||
new object[] { Language.Persian, 33 },
|
new object[] { Language.Persian, 33 },
|
||||||
new object[] { Language.Bengali, 34 },
|
new object[] { Language.Bengali, 34 },
|
||||||
|
new object[] { Language.Slovak, 35 },
|
||||||
|
new object[] { Language.Latvian, 36 },
|
||||||
};
|
};
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
|
using FluentAssertions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.CustomFormats;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.Organizer;
|
||||||
|
using NzbDrone.Core.Qualities;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class EditionTagsFixture : CoreTest<FileNameBuilder>
|
||||||
|
{
|
||||||
|
private Movie _movie;
|
||||||
|
private MovieFile _movieFile;
|
||||||
|
private NamingConfig _namingConfig;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_movie = Builder<Movie>
|
||||||
|
.CreateNew()
|
||||||
|
.With(s => s.Title = "South Park")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_movieFile = new MovieFile { Quality = new QualityModel(), ReleaseGroup = "SonarrTest" };
|
||||||
|
|
||||||
|
_namingConfig = NamingConfig.Default;
|
||||||
|
_namingConfig.RenameMovies = true;
|
||||||
|
|
||||||
|
Mocker.GetMock<INamingConfigService>()
|
||||||
|
.Setup(c => c.GetConfig()).Returns(_namingConfig);
|
||||||
|
|
||||||
|
Mocker.GetMock<IQualityDefinitionService>()
|
||||||
|
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
|
||||||
|
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
|
||||||
|
|
||||||
|
Mocker.GetMock<ICustomFormatService>()
|
||||||
|
.Setup(v => v.All())
|
||||||
|
.Returns(new List<CustomFormat>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_add_edition_tag()
|
||||||
|
{
|
||||||
|
_movieFile.Edition = "Uncut";
|
||||||
|
_namingConfig.StandardMovieFormat = "{Movie Title} [{Edition Tags}]";
|
||||||
|
|
||||||
|
Subject.BuildFileName(_movie, _movieFile)
|
||||||
|
.Should().Be("South Park [Uncut]");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("{Movie Title} {edition-{Edition Tags}}")]
|
||||||
|
public void should_conditional_hide_edition_tags_in_plex_format(string movieFormat)
|
||||||
|
{
|
||||||
|
_movieFile.Edition = "";
|
||||||
|
_namingConfig.StandardMovieFormat = movieFormat;
|
||||||
|
|
||||||
|
Subject.BuildFileName(_movie, _movieFile)
|
||||||
|
.Should().Be("South Park");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,9 +20,7 @@ using NzbDrone.Core.Test.Framework;
|
|||||||
|
|
||||||
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
{
|
{
|
||||||
[Platform(Exclude = "Win")]
|
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
|
|
||||||
public class FileNameBuilderFixture : CoreTest<FileNameBuilder>
|
public class FileNameBuilderFixture : CoreTest<FileNameBuilder>
|
||||||
{
|
{
|
||||||
private Movie _movie;
|
private Movie _movie;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NUnit.Framework.Internal;
|
using NUnit.Framework.Internal;
|
||||||
@@ -47,5 +47,25 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
|||||||
Subject.GetMovieFolder(_movie)
|
Subject.GetMovieFolder(_movie)
|
||||||
.Should().Be($"Movie Title ({_movie.TmdbId})");
|
.Should().Be($"Movie Title ({_movie.TmdbId})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_add_imdb_tag()
|
||||||
|
{
|
||||||
|
_namingConfig.MovieFolderFormat = "{Movie Title} {imdb-{ImdbId}}";
|
||||||
|
|
||||||
|
Subject.GetMovieFolder(_movie)
|
||||||
|
.Should().Be($"Movie Title {{imdb-{_movie.ImdbId}}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_skip_imdb_tag_if_null()
|
||||||
|
{
|
||||||
|
_namingConfig.MovieFolderFormat = "{Movie Title} {imdb-{ImdbId}}";
|
||||||
|
|
||||||
|
_movie.ImdbId = null;
|
||||||
|
|
||||||
|
Subject.GetMovieFolder(_movie)
|
||||||
|
.Should().Be($"Movie Title");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/NzbDrone.Core.Test/ParserTests/AnimeVersionFixture.cs
Normal file
24
src/NzbDrone.Core.Test/ParserTests/AnimeVersionFixture.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.Parser;
|
||||||
|
using NzbDrone.Core.Qualities;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.ParserTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class AnimeVersionFixture : CoreTest
|
||||||
|
{
|
||||||
|
[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)]
|
||||||
|
public void should_be_able_to_parse_repack(string title, int version)
|
||||||
|
{
|
||||||
|
var result = QualityParser.ParseQuality(title);
|
||||||
|
result.Revision.Version.Should().Be(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,22 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
result.Languages.Should().BeEquivalentTo(Language.Unknown);
|
result.Languages.Should().BeEquivalentTo(Language.Unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase("Movie Title - 2022.en.sub")]
|
||||||
|
[TestCase("Movie Title - 2022.EN.sub")]
|
||||||
|
[TestCase("Movie Title - 2022.eng.sub")]
|
||||||
|
[TestCase("Movie Title - 2022.ENG.sub")]
|
||||||
|
[TestCase("Movie Title - 2022.English.sub")]
|
||||||
|
[TestCase("Movie Title - 2022.english.sub")]
|
||||||
|
[TestCase("Movie Title - 2022.en.cc.sub")]
|
||||||
|
[TestCase("Movie Title - 2022.en.sdh.sub")]
|
||||||
|
[TestCase("Movie Title - 2022.en.forced.sub")]
|
||||||
|
[TestCase("Movie Title - 2022.en.sdh.forced.sub")]
|
||||||
|
public void should_parse_subtitle_language_english(string fileName)
|
||||||
|
{
|
||||||
|
var result = LanguageParser.ParseSubtitleLanguage(fileName);
|
||||||
|
result.Should().Be(Language.English);
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase("Movie.Title.1994.French.1080p.XviD-LOL")]
|
[TestCase("Movie.Title.1994.French.1080p.XviD-LOL")]
|
||||||
[TestCase("Movie Title : Other Title 2011 AVC.1080p.Blu-ray HD.VOSTFR.VFF")]
|
[TestCase("Movie Title : Other Title 2011 AVC.1080p.Blu-ray HD.VOSTFR.VFF")]
|
||||||
[TestCase("Movie Title - Other Title 2011 Bluray 4k HDR HEVC AC3 VFF")]
|
[TestCase("Movie Title - Other Title 2011 Bluray 4k HDR HEVC AC3 VFF")]
|
||||||
@@ -342,6 +358,26 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
result.Languages.Should().BeEquivalentTo(Language.Bengali);
|
result.Languages.Should().BeEquivalentTo(Language.Bengali);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase("Movie.Title.1994.HDTV.x264.SK-iCZi")]
|
||||||
|
[TestCase("Movie.Title.2019.1080p.HDTV.x265.iNTERNAL.SK-iCZi")]
|
||||||
|
[TestCase("Movie.Title.2018.SLOVAK.DUAL.2160p.UHD.BluRay.x265-iCZi")]
|
||||||
|
[TestCase("Movie.Title.1990.SLOVAK.HDTV.x264-iCZi")]
|
||||||
|
public void should_parse_language_slovak(string postTitle)
|
||||||
|
{
|
||||||
|
var result = Parser.Parser.ParseMovieTitle(postTitle);
|
||||||
|
result.Languages.Should().BeEquivalentTo(Language.Slovak);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Movie.Title.2022.LV.WEBRip.XviD-LOL")]
|
||||||
|
[TestCase("Movie.Title.2022.lv.WEBRip.XviD-LOL")]
|
||||||
|
[TestCase("Movie.Title.2022.LATVIAN.WEBRip.XviD-LOL")]
|
||||||
|
[TestCase("Movie.Title.2022.Latvian.WEBRip.XviD-LOL")]
|
||||||
|
public void should_parse_language_latvian(string postTitle)
|
||||||
|
{
|
||||||
|
var result = Parser.Parser.ParseMovieTitle(postTitle);
|
||||||
|
result.Languages.Should().BeEquivalentTo(Language.Latvian);
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase("Movie.Title.en.sub")]
|
[TestCase("Movie.Title.en.sub")]
|
||||||
[TestCase("Movie Title.eng.sub")]
|
[TestCase("Movie Title.eng.sub")]
|
||||||
[TestCase("Movie.Title.eng.forced.sub")]
|
[TestCase("Movie.Title.eng.forced.sub")]
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
[TestCase("A.I.Artificial.Movie.(2001)", "A.I. Artificial Movie")]
|
[TestCase("A.I.Artificial.Movie.(2001)", "A.I. Artificial Movie")]
|
||||||
[TestCase("A.Movie.Name.(1998)", "A Movie Name")]
|
[TestCase("A.Movie.Name.(1998)", "A Movie Name")]
|
||||||
[TestCase("www.Torrenting.com - Movie.2008.720p.X264-DIMENSION", "Movie")]
|
[TestCase("www.Torrenting.com - Movie.2008.720p.X264-DIMENSION", "Movie")]
|
||||||
|
[TestCase("www.5MovieRulz.tc - Movie (2000) Malayalam HQ HDRip - x264 - AAC - 700MB.mkv", "Movie")]
|
||||||
[TestCase("Movie: The Movie World 2013", "Movie: The Movie World")]
|
[TestCase("Movie: The Movie World 2013", "Movie: The Movie World")]
|
||||||
[TestCase("Movie.The.Final.Chapter.2016", "Movie The Final Chapter")]
|
[TestCase("Movie.The.Final.Chapter.2016", "Movie The Final Chapter")]
|
||||||
[TestCase("Der.Movie.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", "Der Movie James")]
|
[TestCase("Der.Movie.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", "Der Movie James")]
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using NzbDrone.Core.Test.Framework;
|
|||||||
namespace NzbDrone.Core.Test.ParserTests
|
namespace NzbDrone.Core.Test.ParserTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
|
|
||||||
public class QualityParserFixture : CoreTest
|
public class QualityParserFixture : CoreTest
|
||||||
{
|
{
|
||||||
[SetUp]
|
[SetUp]
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
[TestCase("The.Movie.Title.2013.720p.BluRay.x264-ROUGH [PublicHD]", "ROUGH")]
|
[TestCase("The.Movie.Title.2013.720p.BluRay.x264-ROUGH [PublicHD]", "ROUGH")]
|
||||||
[TestCase("Some.Really.Bad.Movie.Title.[2021].1080p.WEB-HDRip.Dual.Audio.[Hindi.[Clean]. .English].x264.AAC.DD.2.0.By.Full4Movies.mkv-xpost", null)]
|
[TestCase("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("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")]
|
||||||
public void should_parse_expected_release_group(string title, string expected)
|
public void should_parse_expected_release_group(string title, string expected)
|
||||||
{
|
{
|
||||||
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);
|
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);
|
||||||
@@ -100,6 +101,10 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
[TestCase("Yet Another Anime Movie 2012 [Kametsu] [Blu-ray][MKV][h264 10-bit][1080p][FLAC 5.1][Dual Audio][Softsubs (Kametsu)]", "Kametsu")]
|
[TestCase("Yet Another Anime Movie 2012 [Kametsu] [Blu-ray][MKV][h264 10-bit][1080p][FLAC 5.1][Dual Audio][Softsubs (Kametsu)]", "Kametsu")]
|
||||||
[TestCase("Another.Anime.Film.Name.2016.JPN.Blu-Ray.Remux.AVC.DTS-MA.BluDragon", "BluDragon")]
|
[TestCase("Another.Anime.Film.Name.2016.JPN.Blu-Ray.Remux.AVC.DTS-MA.BluDragon", "BluDragon")]
|
||||||
[TestCase("A Movie in the Name (1964) (1080p BluRay x265 r00t)", "r00t")]
|
[TestCase("A Movie in the Name (1964) (1080p BluRay x265 r00t)", "r00t")]
|
||||||
|
[TestCase("Movie Title (2022) (2160p ATV WEB-DL Hybrid H265 DV HDR DDP Atmos 5.1 English - HONE)", "HONE")]
|
||||||
|
[TestCase("Movie Title (2009) (2160p PMTP WEB-DL Hybrid H265 DV HDR10+ DDP Atmos 5.1 English - HONE)", "HONE")]
|
||||||
|
[TestCase("Why.Cant.You.Use.Normal.Characters.2021.2160p.UHD.HDR10+.BluRay.TrueHD.Atmos.7.1.x265-ZØNEHD", "ZØNEHD")]
|
||||||
|
[TestCase("Movie.Should.Not.Use.Dots.2022.1080p.BluRay.x265.10bit.Tigole", "Tigole")]
|
||||||
public void should_parse_exception_release_group(string title, string expected)
|
public void should_parse_exception_release_group(string title, string expected)
|
||||||
{
|
{
|
||||||
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);
|
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);
|
||||||
|
|||||||
161
src/NzbDrone.Core.Test/ParserTests/SlugParserFixture.cs
Normal file
161
src/NzbDrone.Core.Test/ParserTests/SlugParserFixture.cs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.ParserTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class SlugParserFixture : CoreTest
|
||||||
|
{
|
||||||
|
[TestCase("tèst", "test")]
|
||||||
|
[TestCase("têst", "test")]
|
||||||
|
[TestCase("tëst", "test")]
|
||||||
|
[TestCase("tËst", "test")]
|
||||||
|
[TestCase("áccent", "accent")]
|
||||||
|
[TestCase("àccent", "accent")]
|
||||||
|
[TestCase("âccent", "accent")]
|
||||||
|
[TestCase("Äccent", "accent")]
|
||||||
|
[TestCase("åccent", "accent")]
|
||||||
|
[TestCase("acceñt", "accent")]
|
||||||
|
[TestCase("ßtest", "test")]
|
||||||
|
[TestCase("œtest", "test")]
|
||||||
|
[TestCase("Œtest", "test")]
|
||||||
|
[TestCase("Øtest", "test")]
|
||||||
|
public void should_replace_accents(string input, string result)
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug(input).Should().Be(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Test'Result")]
|
||||||
|
[TestCase("Test$Result")]
|
||||||
|
[TestCase("Test(Result")]
|
||||||
|
[TestCase("Test)Result")]
|
||||||
|
[TestCase("Test*Result")]
|
||||||
|
[TestCase("Test?Result")]
|
||||||
|
[TestCase("Test/Result")]
|
||||||
|
[TestCase("Test=Result")]
|
||||||
|
[TestCase("Test\\Result")]
|
||||||
|
public void should_replace_special_characters(string input)
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug(input).Should().Be("testresult");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("ThIS IS A MiXeD CaSe SensItIvE ValUe")]
|
||||||
|
public void should_lowercase_capitals(string input)
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug(input).Should().Be("this-is-a-mixed-case-sensitive-value");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("test----")]
|
||||||
|
[TestCase("test____")]
|
||||||
|
[TestCase("test-_--_")]
|
||||||
|
public void should_trim_trailing_dashes_and_underscores(string input)
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug(input).Should().Be("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("test result")]
|
||||||
|
[TestCase("test result")]
|
||||||
|
public void should_replace_spaces_with_dash(string input)
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug(input).Should().Be("test-result");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("test result", "test-result")]
|
||||||
|
[TestCase("test-----result", "test-result")]
|
||||||
|
[TestCase("test_____result", "test_result")]
|
||||||
|
public void should_replace_double_occurence(string input, string result)
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug(input).Should().Be(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Test'Result")]
|
||||||
|
[TestCase("Test$Result")]
|
||||||
|
[TestCase("Test(Result")]
|
||||||
|
[TestCase("Test)Result")]
|
||||||
|
[TestCase("Test*Result")]
|
||||||
|
[TestCase("Test?Result")]
|
||||||
|
[TestCase("Test/Result")]
|
||||||
|
[TestCase("Test=Result")]
|
||||||
|
[TestCase("Test\\Result")]
|
||||||
|
public void should_replace_special_characters_with_dash_when_enabled(string input)
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug(input, true).Should().Be("test-result");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Test'Result")]
|
||||||
|
[TestCase("Test$Result")]
|
||||||
|
[TestCase("Test(Result")]
|
||||||
|
[TestCase("Test)Result")]
|
||||||
|
[TestCase("Test*Result")]
|
||||||
|
[TestCase("Test?Result")]
|
||||||
|
[TestCase("Test/Result")]
|
||||||
|
[TestCase("Test=Result")]
|
||||||
|
[TestCase("Test\\Result")]
|
||||||
|
public void should__not_replace_special_characters_with_dash_when_disabled(string input)
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug(input, false).Should().Be("testresult");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("test----", "-_", "test")]
|
||||||
|
[TestCase("test____", "-_", "test")]
|
||||||
|
[TestCase("test-_-_", "-_", "test")]
|
||||||
|
[TestCase("test----", "-", "test")]
|
||||||
|
[TestCase("test____", "-", "test____")]
|
||||||
|
[TestCase("test-_-_", "-", "test-_-_")]
|
||||||
|
[TestCase("test----", "_", "test----")]
|
||||||
|
[TestCase("test____", "_", "test")]
|
||||||
|
[TestCase("test-_-_", "_", "test-_-")]
|
||||||
|
[TestCase("test----", "", "test----")]
|
||||||
|
[TestCase("test____", "", "test____")]
|
||||||
|
[TestCase("test-_-_", "", "test-_-_")]
|
||||||
|
public void should_trim_trailing_dashes_and_underscores_based_on_list(string input, string trimList, string result)
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug(input, false, trimList, "").Should().Be(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("test----result", "-_", "test-result")]
|
||||||
|
[TestCase("test____result", "-_", "test_result")]
|
||||||
|
[TestCase("test_-_-result", "-_", "test-result")]
|
||||||
|
[TestCase("test-_-_result", "-_", "test_result")]
|
||||||
|
[TestCase("test----result", "-", "test-result")]
|
||||||
|
[TestCase("test____result", "-", "test____result")]
|
||||||
|
[TestCase("test-_-_result", "-", "test-_-_result")]
|
||||||
|
[TestCase("test----result", "_", "test----result")]
|
||||||
|
[TestCase("test____result", "_", "test_result")]
|
||||||
|
[TestCase("test-_-_result", "_", "test-_-_result")]
|
||||||
|
[TestCase("test----result", "", "test----result")]
|
||||||
|
[TestCase("test____result", "", "test____result")]
|
||||||
|
[TestCase("test-_-_result", "", "test-_-_result")]
|
||||||
|
public void should_replace_duplicate_characters_based_on_list(string input, string deduplicateChars, string result)
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug(input, false, "", deduplicateChars).Should().Be(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_handle_null_trim_parameters()
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug("test", false, null, "-_").Should().Be("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_handle_null_dedupe_parameters()
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug("test", false, "-_", null).Should().Be("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_handle_empty_trim_parameters()
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug("test", false, "", "-_").Should().Be("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_handle_empty_dedupe_parameters()
|
||||||
|
{
|
||||||
|
Parser.Parser.ToUrlSlug("test", false, "-_", "").Should().Be("test");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ using Moq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Movies;
|
using NzbDrone.Core.Movies;
|
||||||
using NzbDrone.Core.RootFolders;
|
using NzbDrone.Core.RootFolders;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
@@ -151,5 +152,107 @@ namespace NzbDrone.Core.Test.RootFolderTests
|
|||||||
unmappedFolders.Count.Should().BeGreaterThan(0);
|
unmappedFolders.Count.Should().BeGreaterThan(0);
|
||||||
unmappedFolders.Should().NotContain(u => u.Name == subFolder);
|
unmappedFolders.Should().NotContain(u => u.Name == subFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase("")]
|
||||||
|
[TestCase(null)]
|
||||||
|
public void should_handle_non_configured_recycle_bin(string recycleBinPath)
|
||||||
|
{
|
||||||
|
var rootFolder = Builder<RootFolder>.CreateNew()
|
||||||
|
.With(r => r.Path = @"C:\Test\TV")
|
||||||
|
.Build();
|
||||||
|
if (OsInfo.IsNotWindows)
|
||||||
|
{
|
||||||
|
rootFolder = Builder<RootFolder>.CreateNew()
|
||||||
|
.With(r => r.Path = @"/Test/TV")
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
var subFolders = new[]
|
||||||
|
{
|
||||||
|
"Series1",
|
||||||
|
"Series2",
|
||||||
|
"Series3"
|
||||||
|
};
|
||||||
|
|
||||||
|
var folders = subFolders.Select(f => Path.Combine(@"C:\Test\TV", f)).ToArray();
|
||||||
|
|
||||||
|
if (OsInfo.IsNotWindows)
|
||||||
|
{
|
||||||
|
folders = subFolders.Select(f => Path.Combine(@"/Test/TV", f)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mocker.GetMock<IConfigService>()
|
||||||
|
.Setup(s => s.RecycleBin)
|
||||||
|
.Returns(recycleBinPath);
|
||||||
|
|
||||||
|
Mocker.GetMock<IRootFolderRepository>()
|
||||||
|
.Setup(s => s.Get(It.IsAny<int>()))
|
||||||
|
.Returns(rootFolder);
|
||||||
|
|
||||||
|
Mocker.GetMock<IMovieRepository>()
|
||||||
|
.Setup(s => s.AllMoviePaths())
|
||||||
|
.Returns(new Dictionary<int, string>());
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>()
|
||||||
|
.Setup(s => s.GetDirectories(rootFolder.Path))
|
||||||
|
.Returns(folders);
|
||||||
|
|
||||||
|
var unmappedFolders = Subject.Get(rootFolder.Id, true).UnmappedFolders;
|
||||||
|
|
||||||
|
unmappedFolders.Count.Should().Be(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_exclude_recycle_bin()
|
||||||
|
{
|
||||||
|
var rootFolder = Builder<RootFolder>.CreateNew()
|
||||||
|
.With(r => r.Path = @"C:\Test\TV")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
if (OsInfo.IsNotWindows)
|
||||||
|
{
|
||||||
|
rootFolder = Builder<RootFolder>.CreateNew()
|
||||||
|
.With(r => r.Path = @"/Test/TV")
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
var subFolders = new[]
|
||||||
|
{
|
||||||
|
"Series1",
|
||||||
|
"Series2",
|
||||||
|
"Series3",
|
||||||
|
"BIN"
|
||||||
|
};
|
||||||
|
|
||||||
|
var folders = subFolders.Select(f => Path.Combine(@"C:\Test\TV", f)).ToArray();
|
||||||
|
|
||||||
|
if (OsInfo.IsNotWindows)
|
||||||
|
{
|
||||||
|
folders = subFolders.Select(f => Path.Combine(@"/Test/TV", f)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
var recycleFolder = Path.Combine(OsInfo.IsNotWindows ? @"/Test/TV" : @"C:\Test\TV", "BIN");
|
||||||
|
|
||||||
|
Mocker.GetMock<IConfigService>()
|
||||||
|
.Setup(s => s.RecycleBin)
|
||||||
|
.Returns(recycleFolder);
|
||||||
|
|
||||||
|
Mocker.GetMock<IRootFolderRepository>()
|
||||||
|
.Setup(s => s.Get(It.IsAny<int>()))
|
||||||
|
.Returns(rootFolder);
|
||||||
|
|
||||||
|
Mocker.GetMock<IMovieRepository>()
|
||||||
|
.Setup(s => s.AllMoviePaths())
|
||||||
|
.Returns(new Dictionary<int, string>());
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>()
|
||||||
|
.Setup(s => s.GetDirectories(rootFolder.Path))
|
||||||
|
.Returns(folders);
|
||||||
|
|
||||||
|
var unmappedFolders = Subject.Get(rootFolder.Id, true).UnmappedFolders;
|
||||||
|
|
||||||
|
unmappedFolders.Count.Should().Be(3);
|
||||||
|
unmappedFolders.Should().NotContain(u => u.Name == "BIN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -428,6 +428,8 @@ namespace NzbDrone.Core.Configuration
|
|||||||
public CertificateValidationType CertificateValidation =>
|
public CertificateValidationType CertificateValidation =>
|
||||||
GetValueEnum("CertificateValidation", CertificateValidationType.Enabled);
|
GetValueEnum("CertificateValidation", CertificateValidationType.Enabled);
|
||||||
|
|
||||||
|
public string ApplicationUrl => GetValue("ApplicationUrl", string.Empty);
|
||||||
|
|
||||||
private string GetValue(string key)
|
private string GetValue(string key)
|
||||||
{
|
{
|
||||||
return GetValue(key, string.Empty);
|
return GetValue(key, string.Empty);
|
||||||
|
|||||||
@@ -104,5 +104,6 @@ namespace NzbDrone.Core.Configuration
|
|||||||
int BackupRetention { get; }
|
int BackupRetention { get; }
|
||||||
|
|
||||||
CertificateValidationType CertificateValidation { get; }
|
CertificateValidationType CertificateValidation { get; }
|
||||||
|
string ApplicationUrl { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace NzbDrone.Core.CustomFormats
|
|||||||
|
|
||||||
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo)
|
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo)
|
||||||
{
|
{
|
||||||
var comparedLanguage = movieInfo != null && Name == "Original" && movieInfo.ExtraInfo.ContainsKey("OriginalLanguage")
|
var comparedLanguage = movieInfo != null && Value == Language.Original.Id && movieInfo.ExtraInfo.ContainsKey("OriginalLanguage")
|
||||||
? (Language)movieInfo.ExtraInfo["OriginalLanguage"]
|
? (Language)movieInfo.ExtraInfo["OriginalLanguage"]
|
||||||
: (Language)Value;
|
: (Language)Value;
|
||||||
return movieInfo?.Languages?.Contains(comparedLanguage) ?? false;
|
return movieInfo?.Languages?.Contains(comparedLanguage) ?? false;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.CustomFormats
|
|||||||
protected Regex _regex;
|
protected Regex _regex;
|
||||||
protected string _raw;
|
protected string _raw;
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "Regular Expression")]
|
[FieldDefinition(1, Label = "Regular Expression", HelpText = "Custom Format RegEx is Case Insensitive")]
|
||||||
public string Value
|
public string Value
|
||||||
{
|
{
|
||||||
get => _raw;
|
get => _raw;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace NzbDrone.Core.CustomFormats
|
|||||||
{
|
{
|
||||||
public SizeSpecificationValidator()
|
public SizeSpecificationValidator()
|
||||||
{
|
{
|
||||||
RuleFor(c => c.Min).GreaterThan(0);
|
RuleFor(c => c.Min).GreaterThanOrEqualTo(0);
|
||||||
RuleFor(c => c.Max).GreaterThan(c => c.Min);
|
RuleFor(c => c.Max).GreaterThan(c => c.Min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FluentMigrator;
|
||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
{
|
||||||
|
[Migration(212)]
|
||||||
|
public class postgres_update_timestamp_columns_to_with_timezone : NzbDroneMigrationBase
|
||||||
|
{
|
||||||
|
protected override void MainDbUpgrade()
|
||||||
|
{
|
||||||
|
Alter.Table("Blocklist").AlterColumn("Date").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("Blocklist").AlterColumn("PublishedDate").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("Collections").AlterColumn("Added").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("Collections").AlterColumn("LastInfoSync").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("Commands").AlterColumn("QueuedAt").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("Commands").AlterColumn("StartedAt").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("Commands").AlterColumn("EndedAt").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("DownloadClientStatus").AlterColumn("InitialFailure").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("DownloadClientStatus").AlterColumn("MostRecentFailure").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("DownloadClientStatus").AlterColumn("DisabledTill").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("DownloadHistory").AlterColumn("Date").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("ExtraFiles").AlterColumn("Added").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("ExtraFiles").AlterColumn("LastUpdated").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("History").AlterColumn("Date").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("ImportListStatus").AlterColumn("InitialFailure").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("ImportListStatus").AlterColumn("MostRecentFailure").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("ImportListStatus").AlterColumn("DisabledTill").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("IndexerStatus").AlterColumn("InitialFailure").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("IndexerStatus").AlterColumn("MostRecentFailure").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("IndexerStatus").AlterColumn("DisabledTill").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("IndexerStatus").AlterColumn("CookiesExpirationDate").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("MetadataFiles").AlterColumn("LastUpdated").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("MetadataFiles").AlterColumn("Added").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("MovieFiles").AlterColumn("DateAdded").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("MovieMetadata").AlterColumn("DigitalRelease").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("MovieMetadata").AlterColumn("InCinemas").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("MovieMetadata").AlterColumn("LastInfoSync").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("MovieMetadata").AlterColumn("PhysicalRelease").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("Movies").AlterColumn("Added").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("PendingReleases").AlterColumn("Added").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("ScheduledTasks").AlterColumn("LastExecution").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("ScheduledTasks").AlterColumn("LastStartTime").AsDateTimeOffset().Nullable();
|
||||||
|
Alter.Table("SubtitleFiles").AlterColumn("Added").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("SubtitleFiles").AlterColumn("LastUpdated").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("VersionInfo").AlterColumn("AppliedOn").AsDateTimeOffset().Nullable();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LogDbUpgrade()
|
||||||
|
{
|
||||||
|
Alter.Table("Logs").AlterColumn("Time").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("UpdateHistory").AlterColumn("Date").AsDateTimeOffset().NotNullable();
|
||||||
|
Alter.Table("VersionInfo").AlterColumn("AppliedOn").AsDateTimeOffset().Nullable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using FluentMigrator;
|
||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
{
|
||||||
|
[Migration(214)]
|
||||||
|
public class add_language_tags_to_subtitle_files : NzbDroneMigrationBase
|
||||||
|
{
|
||||||
|
protected override void MainDbUpgrade()
|
||||||
|
{
|
||||||
|
Alter.Table("SubtitleFiles").AddColumn("LanguageTags").AsString().Nullable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,23 @@
|
|||||||
using System;
|
using System;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using NzbDrone.Core.Qualities;
|
||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
public class RepackSpecification : IDecisionEngineSpecification
|
public class RepackSpecification : IDecisionEngineSpecification
|
||||||
{
|
{
|
||||||
private readonly UpgradableSpecification _upgradableSpecification;
|
private readonly UpgradableSpecification _upgradableSpecification;
|
||||||
|
private readonly IConfigService _configService;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public RepackSpecification(UpgradableSpecification upgradableSpecification, Logger logger)
|
public RepackSpecification(UpgradableSpecification upgradableSpecification, IConfigService configService, Logger logger)
|
||||||
{
|
{
|
||||||
_upgradableSpecification = upgradableSpecification;
|
_upgradableSpecification = upgradableSpecification;
|
||||||
|
_configService = configService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,11 +26,19 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||||||
|
|
||||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||||
{
|
{
|
||||||
|
var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks;
|
||||||
|
|
||||||
if (!subject.ParsedMovieInfo.Quality.Revision.IsRepack)
|
if (!subject.ParsedMovieInfo.Quality.Revision.IsRepack)
|
||||||
{
|
{
|
||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer)
|
||||||
|
{
|
||||||
|
_logger.Debug("Repacks are not preferred, skipping check");
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
if (subject.Movie.MovieFileId != 0)
|
if (subject.Movie.MovieFileId != 0)
|
||||||
{
|
{
|
||||||
var file = subject.Movie.MovieFile;
|
var file = subject.Movie.MovieFile;
|
||||||
@@ -36,6 +48,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||||||
var releaseGroup = subject.ParsedMovieInfo.ReleaseGroup;
|
var releaseGroup = subject.ParsedMovieInfo.ReleaseGroup;
|
||||||
var fileReleaseGroup = file.ReleaseGroup;
|
var fileReleaseGroup = file.ReleaseGroup;
|
||||||
|
|
||||||
|
if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotUpgrade)
|
||||||
|
{
|
||||||
|
_logger.Debug("Auto downloading of repacks is disabled");
|
||||||
|
return Decision.Reject("Repack downloading is disabled");
|
||||||
|
}
|
||||||
|
|
||||||
if (fileReleaseGroup.IsNullOrWhiteSpace())
|
if (fileReleaseGroup.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
return Decision.Reject("Unable to determine release group for the existing file");
|
return Decision.Reject("Unable to determine release group for the existing file");
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Core.Extras.Files;
|
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
@@ -31,7 +30,6 @@ namespace NzbDrone.Core.Extras
|
|||||||
public void Handle(MovieScannedEvent message)
|
public void Handle(MovieScannedEvent message)
|
||||||
{
|
{
|
||||||
var movie = message.Movie;
|
var movie = message.Movie;
|
||||||
var extraFiles = new List<ExtraFile>();
|
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(movie.Path))
|
if (!_diskProvider.FolderExists(movie.Path))
|
||||||
{
|
{
|
||||||
@@ -43,17 +41,16 @@ namespace NzbDrone.Core.Extras
|
|||||||
var filesOnDisk = _diskScanService.GetNonVideoFiles(movie.Path);
|
var filesOnDisk = _diskScanService.GetNonVideoFiles(movie.Path);
|
||||||
var possibleExtraFiles = _diskScanService.FilterPaths(movie.Path, filesOnDisk, false);
|
var possibleExtraFiles = _diskScanService.FilterPaths(movie.Path, filesOnDisk, false);
|
||||||
|
|
||||||
var filteredFiles = possibleExtraFiles;
|
|
||||||
var importedFiles = new List<string>();
|
var importedFiles = new List<string>();
|
||||||
|
|
||||||
foreach (var existingExtraFileImporter in _existingExtraFileImporters)
|
foreach (var existingExtraFileImporter in _existingExtraFileImporters)
|
||||||
{
|
{
|
||||||
var imported = existingExtraFileImporter.ProcessFiles(movie, filteredFiles, importedFiles);
|
var imported = existingExtraFileImporter.ProcessFiles(movie, possibleExtraFiles, importedFiles);
|
||||||
|
|
||||||
importedFiles.AddRange(imported.Select(f => Path.Combine(movie.Path, f.RelativePath)));
|
importedFiles.AddRange(imported.Select(f => Path.Combine(movie.Path, f.RelativePath)));
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Info("Found {0} extra files", extraFiles.Count);
|
_logger.Info("Found {0} possible extra files, imported {1} files.", possibleExtraFiles.Count, importedFiles.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ namespace NzbDrone.Core.Extras
|
|||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
private readonly List<IManageExtraFiles> _extraFileManagers;
|
private readonly List<IManageExtraFiles> _extraFileManagers;
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public ExtraService(IMediaFileService mediaFileService,
|
public ExtraService(IMediaFileService mediaFileService,
|
||||||
IMovieService movieService,
|
IMovieService movieService,
|
||||||
@@ -45,7 +44,6 @@ namespace NzbDrone.Core.Extras
|
|||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_extraFileManagers = extraFileManagers.OrderBy(e => e.Order).ToList();
|
_extraFileManagers = extraFileManagers.OrderBy(e => e.Order).ToList();
|
||||||
_logger = logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ImportMovie(LocalMovie localMovie, MovieFile movieFile, bool isReadOnly)
|
public void ImportMovie(LocalMovie localMovie, MovieFile movieFile, bool isReadOnly)
|
||||||
@@ -62,61 +60,42 @@ namespace NzbDrone.Core.Extras
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourcePath = localMovie.Path;
|
var folderSearchOption = localMovie.FolderMovieInfo == null
|
||||||
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
|
? SearchOption.TopDirectoryOnly
|
||||||
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
|
: SearchOption.AllDirectories;
|
||||||
var files = _diskProvider.GetFiles(sourceFolder, SearchOption.AllDirectories).Where(f => f != localMovie.Path);
|
|
||||||
|
|
||||||
var wantedExtensions = _configService.ExtraFileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
var wantedExtensions = _configService.ExtraFileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
.Select(e => e.Trim(' ', '.'))
|
.Select(e => e.Trim(' ', '.')
|
||||||
|
.Insert(0, "."))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase)).ToList();
|
var sourceFolder = _diskProvider.GetParentFolder(localMovie.Path);
|
||||||
var filteredFilenames = new List<string>();
|
var files = _diskProvider.GetFiles(sourceFolder, folderSearchOption);
|
||||||
var hasNfo = false;
|
var managedFiles = _extraFileManagers.Select((i) => new List<string>()).ToArray();
|
||||||
|
|
||||||
foreach (var matchingFilename in matchingFilenames)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
// Filter out duplicate NFO files
|
var extension = Path.GetExtension(file);
|
||||||
if (matchingFilename.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase))
|
var matchingExtension = wantedExtensions.FirstOrDefault(e => e.Equals(extension));
|
||||||
{
|
|
||||||
if (hasNfo)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasNfo = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredFilenames.Add(matchingFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var matchingFilename in filteredFilenames)
|
|
||||||
{
|
|
||||||
var matchingExtension = wantedExtensions.FirstOrDefault(e => matchingFilename.EndsWith(e));
|
|
||||||
|
|
||||||
if (matchingExtension == null)
|
if (matchingExtension == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
for (int i = 0; i < _extraFileManagers.Count; i++)
|
||||||
{
|
{
|
||||||
foreach (var extraFileManager in _extraFileManagers)
|
if (_extraFileManagers[i].CanImportFile(localMovie, movieFile, file, extension, isReadOnly))
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(matchingFilename);
|
managedFiles[i].Add(file);
|
||||||
var extraFile = extraFileManager.Import(localMovie.Movie, movieFile, matchingFilename, extension, isReadOnly);
|
break;
|
||||||
|
|
||||||
if (extraFile != null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
}
|
||||||
{
|
|
||||||
_logger.Warn(ex, "Failed to import extra file: {0}", matchingFilename);
|
for (int i = 0; i < _extraFileManagers.Count; i++)
|
||||||
}
|
{
|
||||||
|
_extraFileManagers[i].ImportFiles(localMovie, movieFile, managedFiles[i], isReadOnly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions;
|
|||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Movies;
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Extras.Files
|
namespace NzbDrone.Core.Extras.Files
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,8 @@ namespace NzbDrone.Core.Extras.Files
|
|||||||
IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
|
IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
|
||||||
IEnumerable<ExtraFile> CreateAfterMovieFolder(Movie movie, string movieFolder);
|
IEnumerable<ExtraFile> CreateAfterMovieFolder(Movie movie, string movieFolder);
|
||||||
IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
|
IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
|
||||||
ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly);
|
bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly);
|
||||||
|
IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class ExtraFileManager<TExtraFile> : IManageExtraFiles
|
public abstract class ExtraFileManager<TExtraFile> : IManageExtraFiles
|
||||||
@@ -47,7 +49,8 @@ namespace NzbDrone.Core.Extras.Files
|
|||||||
public abstract IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
|
public abstract IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
|
||||||
public abstract IEnumerable<ExtraFile> CreateAfterMovieFolder(Movie movie, string movieFolder);
|
public abstract IEnumerable<ExtraFile> CreateAfterMovieFolder(Movie movie, string movieFolder);
|
||||||
public abstract IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
|
public abstract IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
|
||||||
public abstract ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly);
|
public abstract bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly);
|
||||||
|
public abstract IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly);
|
||||||
|
|
||||||
protected TExtraFile ImportFile(Movie movie, MovieFile movieFile, string path, bool readOnly, string extension, string fileNameSuffix = null)
|
protected TExtraFile ImportFile(Movie movie, MovieFile movieFile, string path, bool readOnly, string extension, string fileNameSuffix = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -148,11 +148,13 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
|||||||
|
|
||||||
var details = new XElement("movie");
|
var details = new XElement("movie");
|
||||||
|
|
||||||
details.Add(new XElement("title", movieTranslation?.Title ?? movie.Title));
|
var metadataTitle = movieTranslation?.Title ?? movie.Title;
|
||||||
|
|
||||||
|
details.Add(new XElement("title", metadataTitle));
|
||||||
|
|
||||||
details.Add(new XElement("originaltitle", movie.MovieMetadata.Value.OriginalTitle));
|
details.Add(new XElement("originaltitle", movie.MovieMetadata.Value.OriginalTitle));
|
||||||
|
|
||||||
details.Add(new XElement("sorttitle", movie.MovieMetadata.Value.SortTitle));
|
details.Add(new XElement("sorttitle", Parser.Parser.NormalizeTitle(metadataTitle)));
|
||||||
|
|
||||||
if (movie.MovieMetadata.Value.Ratings.Tmdb?.Votes > 0 || movie.MovieMetadata.Value.Ratings.Imdb?.Votes > 0)
|
if (movie.MovieMetadata.Value.Ratings.Tmdb?.Votes > 0 || movie.MovieMetadata.Value.Ratings.Imdb?.Votes > 0)
|
||||||
{
|
{
|
||||||
@@ -261,7 +263,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
|||||||
|
|
||||||
details.Add(new XElement("country"));
|
details.Add(new XElement("country"));
|
||||||
|
|
||||||
if (movie.MovieMetadata.Value.CollectionTitle != null)
|
if (Settings.AddCollectionName && movie.MovieMetadata.Value.CollectionTitle != null)
|
||||||
{
|
{
|
||||||
var setElement = new XElement("set");
|
var setElement = new XElement("set");
|
||||||
|
|
||||||
@@ -305,6 +307,8 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
|||||||
|
|
||||||
details.Add(new XElement("trailer", "plugin://plugin.video.youtube/play/?video_id=" + movie.MovieMetadata.Value.YouTubeTrailerId));
|
details.Add(new XElement("trailer", "plugin://plugin.video.youtube/play/?video_id=" + movie.MovieMetadata.Value.YouTubeTrailerId));
|
||||||
|
|
||||||
|
details.Add(new XElement("watched", watched));
|
||||||
|
|
||||||
if (movieFile.MediaInfo != null)
|
if (movieFile.MediaInfo != null)
|
||||||
{
|
{
|
||||||
var sceneName = movieFile.GetSceneOrFileName();
|
var sceneName = movieFile.GetSceneOrFileName();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
|||||||
MovieMetadataLanguage = (int)Language.English;
|
MovieMetadataLanguage = (int)Language.English;
|
||||||
MovieImages = true;
|
MovieImages = true;
|
||||||
UseMovieNfo = false;
|
UseMovieNfo = false;
|
||||||
|
AddCollectionName = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "Movie Metadata", Type = FieldType.Checkbox)]
|
[FieldDefinition(0, Label = "Movie Metadata", Type = FieldType.Checkbox)]
|
||||||
@@ -41,6 +42,9 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
|||||||
[FieldDefinition(4, Label = "Use Movie.nfo", Type = FieldType.Checkbox, HelpText = "Radarr will write metadata to movie.nfo instead of the default <movie-filename>.nfo")]
|
[FieldDefinition(4, Label = "Use Movie.nfo", Type = FieldType.Checkbox, HelpText = "Radarr will write metadata to movie.nfo instead of the default <movie-filename>.nfo")]
|
||||||
public bool UseMovieNfo { get; set; }
|
public bool UseMovieNfo { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(5, Label = "Collection Name", Type = FieldType.Checkbox, HelpText = "Radarr will write the collection name to the .nfo file", Advanced = true)]
|
||||||
|
public bool AddCollectionName { get; set; }
|
||||||
|
|
||||||
public bool IsValid => true;
|
public bool IsValid => true;
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public NzbDroneValidationResult Validate()
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using NzbDrone.Core.Extras.Metadata.Files;
|
|||||||
using NzbDrone.Core.Extras.Others;
|
using NzbDrone.Core.Extras.Others;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Movies;
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Extras.Metadata
|
namespace NzbDrone.Core.Extras.Metadata
|
||||||
{
|
{
|
||||||
@@ -191,9 +192,14 @@ namespace NzbDrone.Core.Extras.Metadata
|
|||||||
return movedFiles;
|
return movedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly)
|
public override bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly)
|
||||||
{
|
{
|
||||||
return null;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly)
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<ExtraFile>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MetadataFile> GetMetadataFilesForConsumer(IMetadata consumer, List<MetadataFile> movieMetadata)
|
private List<MetadataFile> GetMetadataFilesForConsumer(IMetadata consumer, List<MetadataFile> movieMetadata)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
@@ -7,13 +9,16 @@ using NzbDrone.Core.Configuration;
|
|||||||
using NzbDrone.Core.Extras.Files;
|
using NzbDrone.Core.Extras.Files;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Movies;
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Extras.Others
|
namespace NzbDrone.Core.Extras.Others
|
||||||
{
|
{
|
||||||
public class OtherExtraService : ExtraFileManager<OtherExtraFile>
|
public class OtherExtraService : ExtraFileManager<OtherExtraFile>
|
||||||
{
|
{
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IOtherExtraFileService _otherExtraFileService;
|
private readonly IOtherExtraFileService _otherExtraFileService;
|
||||||
private readonly IMediaFileAttributeService _mediaFileAttributeService;
|
private readonly IMediaFileAttributeService _mediaFileAttributeService;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public OtherExtraService(IConfigService configService,
|
public OtherExtraService(IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
@@ -23,8 +28,10 @@ namespace NzbDrone.Core.Extras.Others
|
|||||||
Logger logger)
|
Logger logger)
|
||||||
: base(configService, diskProvider, diskTransferService, logger)
|
: base(configService, diskProvider, diskTransferService, logger)
|
||||||
{
|
{
|
||||||
|
_diskProvider = diskProvider;
|
||||||
_otherExtraFileService = otherExtraFileService;
|
_otherExtraFileService = otherExtraFileService;
|
||||||
_mediaFileAttributeService = mediaFileAttributeService;
|
_mediaFileAttributeService = mediaFileAttributeService;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Order => 2;
|
public override int Order => 2;
|
||||||
@@ -69,17 +76,79 @@ namespace NzbDrone.Core.Extras.Others
|
|||||||
return movedFiles;
|
return movedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly)
|
public override bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly)
|
||||||
{
|
{
|
||||||
var extraFile = ImportFile(movie, movieFile, path, readOnly, extension, null);
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (extraFile != null)
|
public override IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly)
|
||||||
|
{
|
||||||
|
var importedFiles = new List<ExtraFile>();
|
||||||
|
var filteredFiles = files.Where(f => CanImportFile(localMovie, movieFile, f, Path.GetExtension(f), isReadOnly)).ToList();
|
||||||
|
var sourcePath = localMovie.Path;
|
||||||
|
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
|
||||||
|
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
|
||||||
|
var matchingFiles = new List<string>();
|
||||||
|
var hasNfo = false;
|
||||||
|
|
||||||
|
foreach (var file in filteredFiles)
|
||||||
{
|
{
|
||||||
_mediaFileAttributeService.SetFilePermissions(path);
|
try
|
||||||
_otherExtraFileService.Upsert(extraFile);
|
{
|
||||||
|
// Filter out duplicate NFO files
|
||||||
|
if (file.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
if (hasNfo)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNfo = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filename match
|
||||||
|
if (Path.GetFileNameWithoutExtension(file).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
matchingFiles.Add(file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Movie match
|
||||||
|
var fileMovieInfo = Parser.Parser.ParseMoviePath(file) ?? new ParsedMovieInfo();
|
||||||
|
|
||||||
|
if (fileMovieInfo.MovieTitle == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileMovieInfo.MovieTitle == localMovie.FileMovieInfo.MovieTitle &&
|
||||||
|
fileMovieInfo.Year.Equals(localMovie.FileMovieInfo.Year))
|
||||||
|
{
|
||||||
|
matchingFiles.Add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "Failed to import extra file: {0}", file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return extraFile;
|
foreach (string file in matchingFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var extraFile = ImportFile(localMovie.Movie, movieFile, file, isReadOnly, Path.GetExtension(file), null);
|
||||||
|
_mediaFileAttributeService.SetFilePermissions(file);
|
||||||
|
_otherExtraFileService.Upsert(extraFile);
|
||||||
|
importedFiles.Add(extraFile);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "Failed to import extra file: {0}", file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return importedFiles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Core.Extras.Files;
|
using NzbDrone.Core.Extras.Files;
|
||||||
using NzbDrone.Core.Languages;
|
using NzbDrone.Core.Languages;
|
||||||
|
|
||||||
@@ -5,6 +6,17 @@ namespace NzbDrone.Core.Extras.Subtitles
|
|||||||
{
|
{
|
||||||
public class SubtitleFile : ExtraFile
|
public class SubtitleFile : ExtraFile
|
||||||
{
|
{
|
||||||
|
public SubtitleFile()
|
||||||
|
{
|
||||||
|
LanguageTags = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
public Language Language { get; set; }
|
public Language Language { get; set; }
|
||||||
|
|
||||||
|
public string AggregateString => Language + LanguageTagsAsString + Extension;
|
||||||
|
|
||||||
|
public List<string> LanguageTags { get; set; }
|
||||||
|
|
||||||
|
private string LanguageTagsAsString => string.Join(".", LanguageTags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -9,13 +10,17 @@ using NzbDrone.Core.Configuration;
|
|||||||
using NzbDrone.Core.Extras.Files;
|
using NzbDrone.Core.Extras.Files;
|
||||||
using NzbDrone.Core.Languages;
|
using NzbDrone.Core.Languages;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.MediaFiles.MovieImport;
|
||||||
using NzbDrone.Core.Movies;
|
using NzbDrone.Core.Movies;
|
||||||
using NzbDrone.Core.Parser;
|
using NzbDrone.Core.Parser;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Extras.Subtitles
|
namespace NzbDrone.Core.Extras.Subtitles
|
||||||
{
|
{
|
||||||
public class SubtitleService : ExtraFileManager<SubtitleFile>
|
public class SubtitleService : ExtraFileManager<SubtitleFile>
|
||||||
{
|
{
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
|
private readonly IDetectSample _detectSample;
|
||||||
private readonly ISubtitleFileService _subtitleFileService;
|
private readonly ISubtitleFileService _subtitleFileService;
|
||||||
private readonly IMediaFileAttributeService _mediaFileAttributeService;
|
private readonly IMediaFileAttributeService _mediaFileAttributeService;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
@@ -23,11 +28,14 @@ namespace NzbDrone.Core.Extras.Subtitles
|
|||||||
public SubtitleService(IConfigService configService,
|
public SubtitleService(IConfigService configService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IDiskTransferService diskTransferService,
|
IDiskTransferService diskTransferService,
|
||||||
|
IDetectSample detectSample,
|
||||||
ISubtitleFileService subtitleFileService,
|
ISubtitleFileService subtitleFileService,
|
||||||
IMediaFileAttributeService mediaFileAttributeService,
|
IMediaFileAttributeService mediaFileAttributeService,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(configService, diskProvider, diskTransferService, logger)
|
: base(configService, diskProvider, diskTransferService, logger)
|
||||||
{
|
{
|
||||||
|
_diskProvider = diskProvider;
|
||||||
|
_detectSample = detectSample;
|
||||||
_subtitleFileService = subtitleFileService;
|
_subtitleFileService = subtitleFileService;
|
||||||
_mediaFileAttributeService = mediaFileAttributeService;
|
_mediaFileAttributeService = mediaFileAttributeService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -64,21 +72,16 @@ namespace NzbDrone.Core.Extras.Subtitles
|
|||||||
foreach (var movieFile in movieFiles)
|
foreach (var movieFile in movieFiles)
|
||||||
{
|
{
|
||||||
var groupedExtraFilesForMovieFile = subtitleFiles.Where(m => m.MovieFileId == movieFile.Id)
|
var groupedExtraFilesForMovieFile = subtitleFiles.Where(m => m.MovieFileId == movieFile.Id)
|
||||||
.GroupBy(s => s.Language + s.Extension).ToList();
|
.GroupBy(s => s.AggregateString).ToList();
|
||||||
|
|
||||||
foreach (var group in groupedExtraFilesForMovieFile)
|
foreach (var group in groupedExtraFilesForMovieFile)
|
||||||
{
|
{
|
||||||
var groupCount = group.Count();
|
var groupCount = group.Count();
|
||||||
var copy = 1;
|
var copy = 1;
|
||||||
|
|
||||||
if (groupCount > 1)
|
|
||||||
{
|
|
||||||
_logger.Warn("Multiple subtitle files found with the same language and extension for {0}", Path.Combine(movie.Path, movieFile.RelativePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var subtitleFile in group)
|
foreach (var subtitleFile in group)
|
||||||
{
|
{
|
||||||
var suffix = GetSuffix(subtitleFile.Language, copy, groupCount > 1);
|
var suffix = GetSuffix(subtitleFile.Language, copy, subtitleFile.LanguageTags, groupCount > 1);
|
||||||
movedFiles.AddIfNotNull(MoveFile(movie, movieFile, subtitleFile, suffix));
|
movedFiles.AddIfNotNull(MoveFile(movie, movieFile, subtitleFile, suffix));
|
||||||
|
|
||||||
copy++;
|
copy++;
|
||||||
@@ -91,25 +94,141 @@ namespace NzbDrone.Core.Extras.Subtitles
|
|||||||
return movedFiles;
|
return movedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly)
|
public override bool CanImportFile(LocalMovie localEpisode, MovieFile movieFile, string path, string extension, bool readOnly)
|
||||||
{
|
{
|
||||||
if (SubtitleFileExtensions.Extensions.Contains(Path.GetExtension(path)))
|
return SubtitleFileExtensions.Extensions.Contains(extension.ToLowerInvariant());
|
||||||
{
|
|
||||||
var language = LanguageParser.ParseSubtitleLanguage(path);
|
|
||||||
var suffix = GetSuffix(language, 1, false);
|
|
||||||
var subtitleFile = ImportFile(movie, movieFile, path, readOnly, extension, suffix);
|
|
||||||
subtitleFile.Language = language;
|
|
||||||
|
|
||||||
_mediaFileAttributeService.SetFilePermissions(path);
|
|
||||||
_subtitleFileService.Upsert(subtitleFile);
|
|
||||||
|
|
||||||
return subtitleFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetSuffix(Language language, int copy, bool multipleCopies = false)
|
public override IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly)
|
||||||
|
{
|
||||||
|
var importedFiles = new List<SubtitleFile>();
|
||||||
|
|
||||||
|
var filteredFiles = files.Where(f => CanImportFile(localMovie, movieFile, f, Path.GetExtension(f), isReadOnly)).ToList();
|
||||||
|
|
||||||
|
var sourcePath = localMovie.Path;
|
||||||
|
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
|
||||||
|
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
|
||||||
|
|
||||||
|
var matchingFiles = new List<string>();
|
||||||
|
|
||||||
|
foreach (var file in filteredFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Filename match
|
||||||
|
if (Path.GetFileNameWithoutExtension(file).StartsWithIgnoreCase(sourceFileName))
|
||||||
|
{
|
||||||
|
matchingFiles.Add(file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Movie match
|
||||||
|
var fileMovieInfo = Parser.Parser.ParseMoviePath(file) ?? new ParsedMovieInfo();
|
||||||
|
|
||||||
|
if (fileMovieInfo.MovieTitle == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileMovieInfo.MovieTitle == localMovie.FileMovieInfo.MovieTitle &&
|
||||||
|
fileMovieInfo.Year.Equals(localMovie.FileMovieInfo.Year))
|
||||||
|
{
|
||||||
|
matchingFiles.Add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "Failed to import subtitle file: {0}", file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use any sub if only episode in folder
|
||||||
|
if (matchingFiles.Count == 0 && filteredFiles.Count > 0)
|
||||||
|
{
|
||||||
|
var videoFiles = _diskProvider.GetFiles(sourceFolder, SearchOption.AllDirectories)
|
||||||
|
.Where(file => MediaFileExtensions.Extensions.Contains(Path.GetExtension(file)))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (videoFiles.Count() > 2)
|
||||||
|
{
|
||||||
|
return importedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out samples
|
||||||
|
videoFiles = videoFiles.Where(file =>
|
||||||
|
{
|
||||||
|
var sample = _detectSample.IsSample(localMovie.Movie.MovieMetadata, file);
|
||||||
|
|
||||||
|
if (sample == DetectSampleResult.Sample)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
if (videoFiles.Count == 1)
|
||||||
|
{
|
||||||
|
matchingFiles.AddRange(filteredFiles);
|
||||||
|
|
||||||
|
_logger.Warn("Imported any available subtitle file for movie: {0}", localMovie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var subtitleFiles = new List<SubtitleFile>();
|
||||||
|
|
||||||
|
foreach (string file in matchingFiles)
|
||||||
|
{
|
||||||
|
var language = LanguageParser.ParseSubtitleLanguage(file);
|
||||||
|
var extension = Path.GetExtension(file);
|
||||||
|
var languageTags = LanguageParser.ParseLanguageTags(file);
|
||||||
|
var subFile = new SubtitleFile
|
||||||
|
{
|
||||||
|
Language = language,
|
||||||
|
Extension = extension
|
||||||
|
};
|
||||||
|
subFile.LanguageTags = languageTags.ToList();
|
||||||
|
subFile.RelativePath = PathExtensions.GetRelativePath(sourceFolder, file);
|
||||||
|
subtitleFiles.Add(subFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupedSubtitleFiles = subtitleFiles.GroupBy(s => s.AggregateString).ToList();
|
||||||
|
|
||||||
|
foreach (var group in groupedSubtitleFiles)
|
||||||
|
{
|
||||||
|
var groupCount = group.Count();
|
||||||
|
var copy = 1;
|
||||||
|
|
||||||
|
foreach (var file in group)
|
||||||
|
{
|
||||||
|
var path = Path.Combine(sourceFolder, file.RelativePath);
|
||||||
|
var language = file.Language;
|
||||||
|
var extension = file.Extension;
|
||||||
|
var suffix = GetSuffix(language, copy, file.LanguageTags, groupCount > 1);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var subtitleFile = ImportFile(localMovie.Movie, movieFile, path, isReadOnly, extension, suffix);
|
||||||
|
subtitleFile.Language = language;
|
||||||
|
subtitleFile.LanguageTags = file.LanguageTags;
|
||||||
|
|
||||||
|
_mediaFileAttributeService.SetFilePermissions(path);
|
||||||
|
_subtitleFileService.Upsert(subtitleFile);
|
||||||
|
|
||||||
|
importedFiles.Add(subtitleFile);
|
||||||
|
|
||||||
|
copy++;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "Failed to import subtitle file: {0}", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return importedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetSuffix(Language language, int copy, List<string> languageTags, bool multipleCopies = false)
|
||||||
{
|
{
|
||||||
var suffixBuilder = new StringBuilder();
|
var suffixBuilder = new StringBuilder();
|
||||||
|
|
||||||
@@ -125,6 +244,12 @@ namespace NzbDrone.Core.Extras.Subtitles
|
|||||||
suffixBuilder.Append(IsoLanguages.Get(language).TwoLetterCode);
|
suffixBuilder.Append(IsoLanguages.Get(language).TwoLetterCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (languageTags.Any())
|
||||||
|
{
|
||||||
|
suffixBuilder.Append(".");
|
||||||
|
suffixBuilder.Append(string.Join(".", languageTags));
|
||||||
|
}
|
||||||
|
|
||||||
return suffixBuilder.ToString();
|
return suffixBuilder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ namespace NzbDrone.Core.ImportLists
|
|||||||
{
|
{
|
||||||
var mappedMovies = reports.Select(m => _movieSearch.MapMovieToTmdbMovie(new MovieMetadata { Title = m.Title, TmdbId = m.TmdbId, ImdbId = m.ImdbId, Year = m.Year }))
|
var mappedMovies = reports.Select(m => _movieSearch.MapMovieToTmdbMovie(new MovieMetadata { Title = m.Title, TmdbId = m.TmdbId, ImdbId = m.ImdbId, Year = m.Year }))
|
||||||
.Where(x => x != null)
|
.Where(x => x != null)
|
||||||
|
.DistinctBy(x => x.TmdbId)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
_movieMetadataService.UpsertMany(mappedMovies);
|
_movieMetadataService.UpsertMany(mappedMovies);
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ namespace NzbDrone.Core.ImportLists.Radarr
|
|||||||
TagIds = Array.Empty<int>();
|
TagIds = Array.Empty<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "Full URL", HelpText = "URL, including port, of the Radarr V3 instance to import from")]
|
[FieldDefinition(0, Label = "Full URL", HelpText = "URL, including port, of the Radarr instance to import from (Radarr 3.0 or higher)")]
|
||||||
public string BaseUrl { get; set; }
|
public string BaseUrl { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "Apikey of the Radarr V3 instance to import from")]
|
[FieldDefinition(1, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "Apikey of the Radarr instance to import from (Radarr 3.0 or higher)")]
|
||||||
public string ApiKey { get; set; }
|
public string ApiKey { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(2, Type = FieldType.Select, SelectOptionsProviderAction = "getProfiles", Label = "Profiles", HelpText = "Profiles from the source instance to import from")]
|
[FieldDefinition(2, Type = FieldType.Select, SelectOptionsProviderAction = "getProfiles", Label = "Profiles", HelpText = "Profiles from the source instance to import from")]
|
||||||
|
|||||||
@@ -27,7 +27,13 @@ namespace NzbDrone.Core.ImportLists.Trakt.List
|
|||||||
{
|
{
|
||||||
var link = string.Empty;
|
var link = string.Empty;
|
||||||
|
|
||||||
var listName = Parser.Parser.ToUrlSlug(Settings.Listname.Trim());
|
// Trakt slug rules:
|
||||||
|
// - replace all special characters with a dash
|
||||||
|
// - replaces multiple dashes with a single dash
|
||||||
|
// - allows underscore as a valid character
|
||||||
|
// - does not trim underscore from the end
|
||||||
|
// - allows multiple underscores in a row
|
||||||
|
var listName = Parser.Parser.ToUrlSlug(Settings.Listname.Trim(), true, "-", "-");
|
||||||
link += $"users/{Settings.Username.Trim()}/lists/{listName}/items/movies?limit={Settings.Limit}";
|
link += $"users/{Settings.Username.Trim()}/lists/{listName}/items/movies?limit={Settings.Limit}";
|
||||||
|
|
||||||
var request = new ImportListRequest(_traktProxy.BuildTraktRequest(link, HttpMethod.Get, Settings.AccessToken));
|
var request = new ImportListRequest(_traktProxy.BuildTraktRequest(link, HttpMethod.Get, Settings.AccessToken));
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
|
|
||||||
namespace NzbDrone.Core.ImportLists.Trakt.Popular
|
namespace NzbDrone.Core.ImportLists.Trakt.Popular
|
||||||
@@ -9,6 +11,24 @@ namespace NzbDrone.Core.ImportLists.Trakt.Popular
|
|||||||
: base()
|
: base()
|
||||||
{
|
{
|
||||||
RuleFor(c => c.TraktListType).NotNull();
|
RuleFor(c => c.TraktListType).NotNull();
|
||||||
|
|
||||||
|
// Loose validation @TODO
|
||||||
|
RuleFor(c => c.Rating)
|
||||||
|
.Matches(@"^\d+\-\d+$", RegexOptions.IgnoreCase)
|
||||||
|
.When(c => c.Rating.IsNotNullOrWhiteSpace())
|
||||||
|
.WithMessage("Not a valid rating");
|
||||||
|
|
||||||
|
// Any valid certification
|
||||||
|
RuleFor(c => c.Certification)
|
||||||
|
.Matches(@"^\bNR\b|\bG\b|\bPG\b|\bPG\-13\b|\bR\b|\bNC\-17\b$", RegexOptions.IgnoreCase)
|
||||||
|
.When(c => c.Certification.IsNotNullOrWhiteSpace())
|
||||||
|
.WithMessage("Not a valid cerification");
|
||||||
|
|
||||||
|
// Loose validation @TODO
|
||||||
|
RuleFor(c => c.Years)
|
||||||
|
.Matches(@"^\d+(\-\d+)?$", RegexOptions.IgnoreCase)
|
||||||
|
.When(c => c.Years.IsNotNullOrWhiteSpace())
|
||||||
|
.WithMessage("Not a valid year or range of years");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,9 +39,25 @@ namespace NzbDrone.Core.ImportLists.Trakt.Popular
|
|||||||
public TraktPopularSettings()
|
public TraktPopularSettings()
|
||||||
{
|
{
|
||||||
TraktListType = (int)TraktPopularListType.Popular;
|
TraktListType = (int)TraktPopularListType.Popular;
|
||||||
|
Rating = "0-100";
|
||||||
|
Certification = "NR,G,PG,PG-13,R,NC-17";
|
||||||
|
Genres = "";
|
||||||
|
Years = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TraktPopularListType), HelpText = "Type of list you're seeking to import from")]
|
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TraktPopularListType), HelpText = "Type of list you're seeking to import from")]
|
||||||
public int TraktListType { get; set; }
|
public int TraktListType { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Label = "Rating", HelpText = "Filter movies by rating range (0-100)")]
|
||||||
|
public string Rating { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(3, Label = "Certification", HelpText = "Filter movies by a certification (NR,G,PG,PG-13,R,NC-17), (Comma Separated)")]
|
||||||
|
public string Certification { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(4, Label = "Genres", HelpText = "Filter movies by Trakt Genre Slug (Comma Separated) Only for Popular Lists")]
|
||||||
|
public string Genres { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(5, Label = "Years", HelpText = "Filter movies by year or year range")]
|
||||||
|
public string Years { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
@@ -29,24 +28,6 @@ namespace NzbDrone.Core.ImportLists.Trakt
|
|||||||
.WithMessage("Must authenticate with Trakt")
|
.WithMessage("Must authenticate with Trakt")
|
||||||
.When(c => c.AccessToken.IsNotNullOrWhiteSpace() && c.RefreshToken.IsNotNullOrWhiteSpace());
|
.When(c => c.AccessToken.IsNotNullOrWhiteSpace() && c.RefreshToken.IsNotNullOrWhiteSpace());
|
||||||
|
|
||||||
// Loose validation @TODO
|
|
||||||
RuleFor(c => c.Rating)
|
|
||||||
.Matches(@"^\d+\-\d+$", RegexOptions.IgnoreCase)
|
|
||||||
.When(c => c.Rating.IsNotNullOrWhiteSpace())
|
|
||||||
.WithMessage("Not a valid rating");
|
|
||||||
|
|
||||||
// Any valid certification
|
|
||||||
RuleFor(c => c.Certification)
|
|
||||||
.Matches(@"^\bNR\b|\bG\b|\bPG\b|\bPG\-13\b|\bR\b|\bNC\-17\b$", RegexOptions.IgnoreCase)
|
|
||||||
.When(c => c.Certification.IsNotNullOrWhiteSpace())
|
|
||||||
.WithMessage("Not a valid cerification");
|
|
||||||
|
|
||||||
// Loose validation @TODO
|
|
||||||
RuleFor(c => c.Years)
|
|
||||||
.Matches(@"^\d+(\-\d+)?$", RegexOptions.IgnoreCase)
|
|
||||||
.When(c => c.Years.IsNotNullOrWhiteSpace())
|
|
||||||
.WithMessage("Not a valid year or range of years");
|
|
||||||
|
|
||||||
// Limit not smaller than 1 and not larger than 100
|
// Limit not smaller than 1 and not larger than 100
|
||||||
RuleFor(c => c.Limit)
|
RuleFor(c => c.Limit)
|
||||||
.GreaterThan(0)
|
.GreaterThan(0)
|
||||||
@@ -62,10 +43,6 @@ namespace NzbDrone.Core.ImportLists.Trakt
|
|||||||
public TraktSettingsBase()
|
public TraktSettingsBase()
|
||||||
{
|
{
|
||||||
SignIn = "startOAuth";
|
SignIn = "startOAuth";
|
||||||
Rating = "0-100";
|
|
||||||
Certification = "NR,G,PG,PG-13,R,NC-17";
|
|
||||||
Genres = "";
|
|
||||||
Years = "";
|
|
||||||
Limit = 100;
|
Limit = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,18 +61,6 @@ namespace NzbDrone.Core.ImportLists.Trakt
|
|||||||
[FieldDefinition(0, Label = "Auth User", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
[FieldDefinition(0, Label = "Auth User", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
public string AuthUser { get; set; }
|
public string AuthUser { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "Rating", HelpText = "Filter movies by rating range (0-100)")]
|
|
||||||
public string Rating { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(2, Label = "Certification", HelpText = "Filter movies by a certification (NR,G,PG,PG-13,R,NC-17), (Comma Separated)")]
|
|
||||||
public string Certification { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(3, Label = "Genres", HelpText = "Filter movies by Trakt Genre Slug (Comma Separated) Only for Popular Lists")]
|
|
||||||
public string Genres { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(4, Label = "Years", HelpText = "Filter movies by year or year range")]
|
|
||||||
public string Years { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(5, Label = "Limit", HelpText = "Limit the number of movies to get")]
|
[FieldDefinition(5, Label = "Limit", HelpText = "Limit the number of movies to get")]
|
||||||
public int Limit { get; set; }
|
public int Limit { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -42,14 +42,15 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
foreach (var movieId in message.MovieIds)
|
foreach (var movieId in message.MovieIds)
|
||||||
{
|
{
|
||||||
var movie = _movieService.GetMovie(movieId);
|
var movie = _movieService.GetMovie(movieId);
|
||||||
|
var userInvokedSearch = message.Trigger == CommandTrigger.Manual;
|
||||||
|
|
||||||
if (!movie.Monitored && message.Trigger != CommandTrigger.Manual)
|
if (!movie.Monitored && !userInvokedSearch)
|
||||||
{
|
{
|
||||||
_logger.Debug("Movie {0} is not monitored, skipping search", movie.Title);
|
_logger.Debug("Movie {0} is not monitored, skipping search", movie.Title);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var decisions = _releaseSearchService.MovieSearch(movieId, false, false);
|
var decisions = _releaseSearchService.MovieSearch(movieId, userInvokedSearch, false);
|
||||||
downloadedCount += _processDownloadDecisions.ProcessDecisions(decisions).Grabbed.Count;
|
downloadedCount += _processDownloadDecisions.ProcessDecisions(decisions).Grabbed.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,12 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
|||||||
|
|
||||||
if (jsonResponse.Resource.error_code.HasValue)
|
if (jsonResponse.Resource.error_code.HasValue)
|
||||||
{
|
{
|
||||||
if (jsonResponse.Resource.error_code == 20 || jsonResponse.Resource.error_code == 8
|
if (jsonResponse.Resource.error_code == 5 || jsonResponse.Resource.error_code == 8
|
||||||
|| jsonResponse.Resource.error_code == 9 || jsonResponse.Resource.error_code == 10)
|
|| jsonResponse.Resource.error_code == 9 || jsonResponse.Resource.error_code == 10
|
||||||
|
|| jsonResponse.Resource.error_code == 13 || jsonResponse.Resource.error_code == 14
|
||||||
|
|| jsonResponse.Resource.error_code == 20)
|
||||||
{
|
{
|
||||||
// No results or imdbid not found
|
// No results, rate limit, tmdbid not found/invalid, or imdbid not found/invalid
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace NzbDrone.Core.Indexers
|
|||||||
{
|
{
|
||||||
private static readonly SeedCriteriaSettingsValidator Validator = new SeedCriteriaSettingsValidator();
|
private static readonly SeedCriteriaSettingsValidator Validator = new SeedCriteriaSettingsValidator();
|
||||||
|
|
||||||
[FieldDefinition(0, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)]
|
[FieldDefinition(0, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default. Ratio should be at least 1.0 and follow the indexers rules")]
|
||||||
public double? SeedRatio { get; set; }
|
public double? SeedRatio { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(1, Type = FieldType.Number, Label = "Seed Time", Unit = "minutes", HelpText = "The time a torrent should be seeded before stopping, empty is download client's default", Advanced = true)]
|
[FieldDefinition(1, Type = FieldType.Number, Label = "Seed Time", Unit = "minutes", HelpText = "The time a torrent should be seeded before stopping, empty is download client's default", Advanced = true)]
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace NzbDrone.Core.Indexers
|
|||||||
{
|
{
|
||||||
if (InfoHashElementName.IsNotNullOrWhiteSpace())
|
if (InfoHashElementName.IsNotNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
return item.FindDecendants(InfoHashElementName).FirstOrDefault().Value;
|
return item.FindDecendants(InfoHashElementName).FirstOrDefault()?.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var magnetUrl = GetMagnetUrl(item);
|
var magnetUrl = GetMagnetUrl(item);
|
||||||
@@ -96,7 +96,7 @@ namespace NzbDrone.Core.Indexers
|
|||||||
{
|
{
|
||||||
if (MagnetElementName.IsNotNullOrWhiteSpace())
|
if (MagnetElementName.IsNotNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
var magnetURL = item.FindDecendants(MagnetElementName).FirstOrDefault().Value;
|
var magnetURL = item.FindDecendants(MagnetElementName).FirstOrDefault()?.Value;
|
||||||
if (magnetURL.IsNotNullOrWhiteSpace() && magnetURL.StartsWith("magnet:"))
|
if (magnetURL.IsNotNullOrWhiteSpace() && magnetURL.StartsWith("magnet:"))
|
||||||
{
|
{
|
||||||
return magnetURL;
|
return magnetURL;
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ namespace NzbDrone.Core.Languages
|
|||||||
public static Language Ukrainian => new Language(32, "Ukrainian");
|
public static Language Ukrainian => new Language(32, "Ukrainian");
|
||||||
public static Language Persian => new Language(33, "Persian");
|
public static Language Persian => new Language(33, "Persian");
|
||||||
public static Language Bengali => new Language(34, "Bengali");
|
public static Language Bengali => new Language(34, "Bengali");
|
||||||
|
public static Language Slovak => new Language(35, "Slovak");
|
||||||
|
public static Language Latvian => new Language(36, "Latvian");
|
||||||
public static Language Any => new Language(-1, "Any");
|
public static Language Any => new Language(-1, "Any");
|
||||||
public static Language Original => new Language(-2, "Original");
|
public static Language Original => new Language(-2, "Original");
|
||||||
|
|
||||||
@@ -149,6 +151,8 @@ namespace NzbDrone.Core.Languages
|
|||||||
Ukrainian,
|
Ukrainian,
|
||||||
Persian,
|
Persian,
|
||||||
Bengali,
|
Bengali,
|
||||||
|
Slovak,
|
||||||
|
Latvian,
|
||||||
Any,
|
Any,
|
||||||
Original
|
Original
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -639,7 +639,7 @@
|
|||||||
"RetryingDownloadInterp": "Herunterladen nochmal versuchen {0} um {1}",
|
"RetryingDownloadInterp": "Herunterladen nochmal versuchen {0} um {1}",
|
||||||
"RemovingTag": "Tag entfernen",
|
"RemovingTag": "Tag entfernen",
|
||||||
"ReleaseWillBeProcessedInterp": "Release wird verarbeitet {0}",
|
"ReleaseWillBeProcessedInterp": "Release wird verarbeitet {0}",
|
||||||
"Queued": "Eingereiht",
|
"Queued": "In der Warteschlange",
|
||||||
"QualityProfileDeleteConfirm": "Qualitätsprofil '{0}' wirklich löschen",
|
"QualityProfileDeleteConfirm": "Qualitätsprofil '{0}' wirklich löschen",
|
||||||
"Pending": "Ausstehend",
|
"Pending": "Ausstehend",
|
||||||
"Paused": "Pausiert",
|
"Paused": "Pausiert",
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
"MarkAsFailedMessageText": "'{0}' wirklich als fehlgeschlagen markieren?",
|
"MarkAsFailedMessageText": "'{0}' wirklich als fehlgeschlagen markieren?",
|
||||||
"Manual": "Manuell",
|
"Manual": "Manuell",
|
||||||
"LogLevelTraceHelpTextWarning": "Trace logging sollte nur kurzzeitig aktiviert werden",
|
"LogLevelTraceHelpTextWarning": "Trace logging sollte nur kurzzeitig aktiviert werden",
|
||||||
"LastDuration": "Letzte Laufzeit",
|
"LastDuration": "Letzte Dauer",
|
||||||
"IncludeRecommendationsHelpText": "Radarrs Empfehlungen in der Entdeckungsansicht mit einbeziehen",
|
"IncludeRecommendationsHelpText": "Radarrs Empfehlungen in der Entdeckungsansicht mit einbeziehen",
|
||||||
"IncludeRadarrRecommendations": "Radarr Empfehlungen einbeziehen",
|
"IncludeRadarrRecommendations": "Radarr Empfehlungen einbeziehen",
|
||||||
"ImportFailedInterp": "Import fehlgeschlagen: {0}",
|
"ImportFailedInterp": "Import fehlgeschlagen: {0}",
|
||||||
@@ -1142,5 +1142,8 @@
|
|||||||
"UnableToLoadCollections": "Sammlungen können nicht geladen werden",
|
"UnableToLoadCollections": "Sammlungen können nicht geladen werden",
|
||||||
"InstanceName": "Instanzname",
|
"InstanceName": "Instanzname",
|
||||||
"InstanceNameHelpText": "Instanzname im Browser-Tab und für Syslog-Anwendungsname",
|
"InstanceNameHelpText": "Instanzname im Browser-Tab und für Syslog-Anwendungsname",
|
||||||
"RottenTomatoesRating": "Tomato Bewertung"
|
"RottenTomatoesRating": "Tomato Bewertung",
|
||||||
|
"TotalMovies": "Filme insgesamt",
|
||||||
|
"ApplicationURL": "Anwendungs-URL",
|
||||||
|
"ApplicationUrlHelpText": "Die externe URL der Anwendung inklusive http(s)://, Port und URL-Basis"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"ApiKey": "API Key",
|
"ApiKey": "API Key",
|
||||||
"AppDataDirectory": "AppData directory",
|
"AppDataDirectory": "AppData directory",
|
||||||
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
|
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
|
||||||
|
"ApplicationURL": "Application URL",
|
||||||
|
"ApplicationUrlHelpText": "This application's external URL including http(s)://, port and URL base",
|
||||||
"Apply": "Apply",
|
"Apply": "Apply",
|
||||||
"ApplyTags": "Apply Tags",
|
"ApplyTags": "Apply Tags",
|
||||||
"ApplyTagsHelpTexts1": "How to apply tags to the selected movies",
|
"ApplyTagsHelpTexts1": "How to apply tags to the selected movies",
|
||||||
@@ -735,7 +737,7 @@
|
|||||||
"QualityOrLangCutoffHasNotBeenMet": "Quality or Language cutoff has not been met",
|
"QualityOrLangCutoffHasNotBeenMet": "Quality or Language cutoff has not been met",
|
||||||
"QualityProfile": "Quality Profile",
|
"QualityProfile": "Quality Profile",
|
||||||
"QualityProfileDeleteConfirm": "Are you sure you want to delete the quality profile {0}",
|
"QualityProfileDeleteConfirm": "Are you sure you want to delete the quality profile {0}",
|
||||||
"QualityProfileInUse": "Can't delete a quality profile that is attached to a movie",
|
"QualityProfileInUse": "Can't delete a quality profile that is attached to a movie, list, or collection",
|
||||||
"QualityProfiles": "Quality Profiles",
|
"QualityProfiles": "Quality Profiles",
|
||||||
"QualitySettings": "Quality Settings",
|
"QualitySettings": "Quality Settings",
|
||||||
"QualitySettingsSummary": "Quality sizes and naming",
|
"QualitySettingsSummary": "Quality sizes and naming",
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
"Search": "Buscar",
|
"Search": "Buscar",
|
||||||
"Scheduled": "Programado",
|
"Scheduled": "Programado",
|
||||||
"SaveChanges": "Guardar Cambios",
|
"SaveChanges": "Guardar Cambios",
|
||||||
"RSSSync": "Sincronización RSS",
|
"RSSSync": "Sincronizar RSS",
|
||||||
"RootFolders": "Carpetas de Origen",
|
"RootFolders": "Carpetas de Origen",
|
||||||
"RootFolderCheckSingleMessage": "La carpeta de origen no existe: {0}",
|
"RootFolderCheckSingleMessage": "La carpeta de origen no existe: {0}",
|
||||||
"RootFolderCheckMultipleMessage": "Varias carpetas de origen no existen: {0}",
|
"RootFolderCheckMultipleMessage": "Varias carpetas de origen no existen: {0}",
|
||||||
@@ -232,7 +232,7 @@
|
|||||||
"OAuthPopupMessage": "Pop-ups bloqueados por su navegador",
|
"OAuthPopupMessage": "Pop-ups bloqueados por su navegador",
|
||||||
"Name": "Nombre",
|
"Name": "Nombre",
|
||||||
"MoveFiles": "Mover Archivos",
|
"MoveFiles": "Mover Archivos",
|
||||||
"Monitored": "Monitoreadas",
|
"Monitored": "Monitoreada",
|
||||||
"Message": "Mensaje",
|
"Message": "Mensaje",
|
||||||
"Location": "Localización",
|
"Location": "Localización",
|
||||||
"Level": "Nivel",
|
"Level": "Nivel",
|
||||||
@@ -461,7 +461,7 @@
|
|||||||
"ShowMonitoredHelpText": "Mostrar el estado de monitoreo debajo del poster",
|
"ShowMonitoredHelpText": "Mostrar el estado de monitoreo debajo del poster",
|
||||||
"ShowCutoffUnmetIconHelpText": "Mostrar el icono para los ficheros cuando no se ha alcanzado el corte",
|
"ShowCutoffUnmetIconHelpText": "Mostrar el icono para los ficheros cuando no se ha alcanzado el corte",
|
||||||
"ShowAsAllDayEvents": "Mostrar como Eventos de Todo el Día",
|
"ShowAsAllDayEvents": "Mostrar como Eventos de Todo el Día",
|
||||||
"ShouldMonitorHelpText": "Si se habilita, las películas añadidas desde esta lista serán también monitoreadas",
|
"ShouldMonitorHelpText": "Si las películas o colecciones en esta lista deberían ser agregadas como monitoreadas",
|
||||||
"SetPermissionsLinuxHelpTextWarning": "Si no estas seguro de lo que hacen estos ajustes, no los modifiques.",
|
"SetPermissionsLinuxHelpTextWarning": "Si no estas seguro de lo que hacen estos ajustes, no los modifiques.",
|
||||||
"SetPermissionsLinuxHelpText": "Debe chmod ser ejecutado una vez los archivos hayan sido importados/renombrados?",
|
"SetPermissionsLinuxHelpText": "Debe chmod ser ejecutado una vez los archivos hayan sido importados/renombrados?",
|
||||||
"SetPermissions": "Ajustar Permisos",
|
"SetPermissions": "Ajustar Permisos",
|
||||||
@@ -614,8 +614,8 @@
|
|||||||
"CleanLibraryLevel": "Limpiar el Nivel de la Librería",
|
"CleanLibraryLevel": "Limpiar el Nivel de la Librería",
|
||||||
"BindAddress": "Dirección de Ligado",
|
"BindAddress": "Dirección de Ligado",
|
||||||
"OpenBrowserOnStart": "Abrir navegador al arrancar",
|
"OpenBrowserOnStart": "Abrir navegador al arrancar",
|
||||||
"OnUpgradeHelpText": "Al Actualizar",
|
"OnUpgradeHelpText": "Al Mejorar La Calidad",
|
||||||
"OnRenameHelpText": "En Renombrado",
|
"OnRenameHelpText": "Al Renombrar",
|
||||||
"OnHealthIssueHelpText": "En Problema de Salud",
|
"OnHealthIssueHelpText": "En Problema de Salud",
|
||||||
"OnGrabHelpText": "Al Capturar",
|
"OnGrabHelpText": "Al Capturar",
|
||||||
"SettingsRuntimeFormat": "Formato de Tiempo de ejecución",
|
"SettingsRuntimeFormat": "Formato de Tiempo de ejecución",
|
||||||
@@ -633,7 +633,7 @@
|
|||||||
"ShownClickToHide": "Mostrado, clic para ocultar",
|
"ShownClickToHide": "Mostrado, clic para ocultar",
|
||||||
"ShowGenres": "Mostrar Géneros",
|
"ShowGenres": "Mostrar Géneros",
|
||||||
"ShowCertification": "Mostrar Certificación",
|
"ShowCertification": "Mostrar Certificación",
|
||||||
"SearchOnAddHelpText": "Buscar películas en esta lista cuando se añadan a Radarr",
|
"SearchOnAddHelpText": "Buscar películas en esta lista cuando se añada a la biblioteca",
|
||||||
"RSSSyncIntervalHelpTextWarning": "Se aplicará a todos los indexers, por favor sigue las reglas de los mismos",
|
"RSSSyncIntervalHelpTextWarning": "Se aplicará a todos los indexers, por favor sigue las reglas de los mismos",
|
||||||
"RSSIsNotSupportedWithThisIndexer": "RSS no son soportadas por este indexer",
|
"RSSIsNotSupportedWithThisIndexer": "RSS no son soportadas por este indexer",
|
||||||
"RetryingDownloadInterp": "Re-intentando descarga {0} en {1}",
|
"RetryingDownloadInterp": "Re-intentando descarga {0} en {1}",
|
||||||
@@ -874,11 +874,11 @@
|
|||||||
"MovieFilesTotaling": "Totalización de archivos de película",
|
"MovieFilesTotaling": "Totalización de archivos de película",
|
||||||
"OnGrab": "Al Capturar",
|
"OnGrab": "Al Capturar",
|
||||||
"OnHealthIssue": "En Problema de Salud",
|
"OnHealthIssue": "En Problema de Salud",
|
||||||
"OnImport": "Al importar",
|
"OnImport": "Al Importar",
|
||||||
"OnLatestVersion": "La última versión de Radarr ya está instalada",
|
"OnLatestVersion": "La última versión de Radarr ya está instalada",
|
||||||
"OnlyTorrent": "Solo Torrent",
|
"OnlyTorrent": "Solo Torrent",
|
||||||
"OnlyUsenet": "Solo Usenet",
|
"OnlyUsenet": "Solo Usenet",
|
||||||
"OnRename": "En Renombrado",
|
"OnRename": "Al Renombrar",
|
||||||
"PreferTorrent": "Prefiero Torrent",
|
"PreferTorrent": "Prefiero Torrent",
|
||||||
"PreferUsenet": "Prefiero Usenet",
|
"PreferUsenet": "Prefiero Usenet",
|
||||||
"Presets": "Preajustes",
|
"Presets": "Preajustes",
|
||||||
@@ -988,12 +988,12 @@
|
|||||||
"Score": "Puntuación",
|
"Score": "Puntuación",
|
||||||
"Script": "Guión",
|
"Script": "Guión",
|
||||||
"SearchCutoffUnmet": "Corte de búsqueda no alcanzado",
|
"SearchCutoffUnmet": "Corte de búsqueda no alcanzado",
|
||||||
"SearchMissing": "Búsqueda que falta",
|
"SearchMissing": "Buscar Faltantes",
|
||||||
"Seconds": "Segundos",
|
"Seconds": "Segundos",
|
||||||
"SelectDotDot": "Seleccione...",
|
"SelectDotDot": "Seleccione...",
|
||||||
"SelectLanguage": "Seleccione el idioma",
|
"SelectLanguage": "Seleccionar Idioma",
|
||||||
"SelectMovie": "Seleccionar película",
|
"SelectMovie": "Seleccionar Película",
|
||||||
"SelectQuality": "Seleccionar calidad",
|
"SelectQuality": "Seleccionar Calidad",
|
||||||
"Small": "Pequeña",
|
"Small": "Pequeña",
|
||||||
"Socks4": "Calcetines4",
|
"Socks4": "Calcetines4",
|
||||||
"Socks5": "Socks5 (soporte TOR)",
|
"Socks5": "Socks5 (soporte TOR)",
|
||||||
@@ -1031,13 +1031,13 @@
|
|||||||
"showCinemaReleaseHelpText": "Mostrar la fecha de estreno en el cine debajo del cartel",
|
"showCinemaReleaseHelpText": "Mostrar la fecha de estreno en el cine debajo del cartel",
|
||||||
"ShowReleaseDate": "Mostrar fecha de lanzamiento",
|
"ShowReleaseDate": "Mostrar fecha de lanzamiento",
|
||||||
"ShowReleaseDateHelpText": "Mostrar la fecha de lanzamiento debajo del cartel",
|
"ShowReleaseDateHelpText": "Mostrar la fecha de lanzamiento debajo del cartel",
|
||||||
"OnMovieDelete": "Al eliminar la película",
|
"OnMovieDelete": "Al Eliminar Película",
|
||||||
"OnMovieDeleteHelpText": "Al eliminar la película",
|
"OnMovieDeleteHelpText": "Al Eliminar Película",
|
||||||
"OnMovieFileDelete": "Al eliminar archivo de película",
|
"OnMovieFileDelete": "Al Eliminar Archivo De Película",
|
||||||
"OnMovieFileDeleteHelpText": "Al eliminar archivo de película",
|
"OnMovieFileDeleteHelpText": "Al Eliminar Archivo De Película",
|
||||||
"OnMovieFileDeleteForUpgrade": "En archivo de película Eliminar para actualizar",
|
"OnMovieFileDeleteForUpgrade": "Al Eliminar Archivo De Pelicula Para Mejorar La Calidad",
|
||||||
"OnMovieFileDeleteForUpgradeHelpText": "En archivo de película Eliminar para actualizar",
|
"OnMovieFileDeleteForUpgradeHelpText": "Al Eliminar Archivo De Pelicula Para Mejorar La Calidad",
|
||||||
"OnUpgrade": "Al Actualizar",
|
"OnUpgrade": "Al Mejorar La Calidad",
|
||||||
"Reddit": "Reddit",
|
"Reddit": "Reddit",
|
||||||
"More": "Más",
|
"More": "Más",
|
||||||
"Download": "Descargar",
|
"Download": "Descargar",
|
||||||
@@ -1064,7 +1064,7 @@
|
|||||||
"List": "Lista",
|
"List": "Lista",
|
||||||
"ManualImportSetReleaseGroup": "Importación manual - Configurar el grupo de lanzamiento",
|
"ManualImportSetReleaseGroup": "Importación manual - Configurar el grupo de lanzamiento",
|
||||||
"NotificationTriggersHelpText": "Seleccione qué eventos deben activar esta notificación",
|
"NotificationTriggersHelpText": "Seleccione qué eventos deben activar esta notificación",
|
||||||
"OnApplicationUpdate": "Al actualizar la aplicación",
|
"OnApplicationUpdate": "Al Actualizar La Aplicación",
|
||||||
"RemotePathMappingCheckFileRemoved": "El archivo {0} fue eliminado a mitad del proceso.",
|
"RemotePathMappingCheckFileRemoved": "El archivo {0} fue eliminado a mitad del proceso.",
|
||||||
"RemotePath": "Ruta remota",
|
"RemotePath": "Ruta remota",
|
||||||
"RemotePathMappingCheckBadDockerPath": "Está utilizando docker; el cliente de descarga {0} coloca las descargas en {1} pero esta no es una ruta válida {2}. Revisa tus mapeos de rutas remotas y la configuración del cliente de descarga.",
|
"RemotePathMappingCheckBadDockerPath": "Está utilizando docker; el cliente de descarga {0} coloca las descargas en {1} pero esta no es una ruta válida {2}. Revisa tus mapeos de rutas remotas y la configuración del cliente de descarga.",
|
||||||
@@ -1072,7 +1072,7 @@
|
|||||||
"RemotePathMappingCheckFilesBadDockerPath": "Está utilizando docker; el cliente de descarga {0} informó de archivos en {1} pero esta no es una ruta válida {2}. Revise sus mapeos de rutas remotas y la configuración del cliente de descarga.",
|
"RemotePathMappingCheckFilesBadDockerPath": "Está utilizando docker; el cliente de descarga {0} informó de archivos en {1} pero esta no es una ruta válida {2}. Revise sus mapeos de rutas remotas y la configuración del cliente de descarga.",
|
||||||
"RemotePathMappingCheckLocalWrongOSPath": "El cliente de descarga local {0} coloca las descargas en {1} pero ésta no es una ruta válida {2}. Revise la configuración de su cliente de descarga.",
|
"RemotePathMappingCheckLocalWrongOSPath": "El cliente de descarga local {0} coloca las descargas en {1} pero ésta no es una ruta válida {2}. Revise la configuración de su cliente de descarga.",
|
||||||
"RemotePathMappingCheckRemoteDownloadClient": "El cliente de descarga remota {0} informó de la existencia de archivos en {1} pero este directorio no parece existir. Probablemente falta mapear la ruta remota.",
|
"RemotePathMappingCheckRemoteDownloadClient": "El cliente de descarga remota {0} informó de la existencia de archivos en {1} pero este directorio no parece existir. Probablemente falta mapear la ruta remota.",
|
||||||
"SelectLanguages": "Seleccionar idiomas",
|
"SelectLanguages": "Seleccionar Idiomas",
|
||||||
"IndexerDownloadClientHelpText": "Especifica qué cliente de descargas se utiliza para las descargas de este indexador",
|
"IndexerDownloadClientHelpText": "Especifica qué cliente de descargas se utiliza para las descargas de este indexador",
|
||||||
"AnnouncedMsg": "Película anunciada",
|
"AnnouncedMsg": "Película anunciada",
|
||||||
"Auto": "Auto",
|
"Auto": "Auto",
|
||||||
@@ -1097,7 +1097,7 @@
|
|||||||
"TmdbRating": "TMDb valoración",
|
"TmdbRating": "TMDb valoración",
|
||||||
"TmdbVotes": "Votos en TMDb",
|
"TmdbVotes": "Votos en TMDb",
|
||||||
"Filters": "Filtros",
|
"Filters": "Filtros",
|
||||||
"OnApplicationUpdateHelpText": "Al actualizar la aplicación",
|
"OnApplicationUpdateHelpText": "Al Actualizar La Aplicación",
|
||||||
"OriginalTitle": "Título original",
|
"OriginalTitle": "Título original",
|
||||||
"OriginalLanguage": "Idioma original",
|
"OriginalLanguage": "Idioma original",
|
||||||
"Rating": "Puntuación",
|
"Rating": "Puntuación",
|
||||||
@@ -1108,7 +1108,7 @@
|
|||||||
"RemotePathMappingCheckFilesLocalWrongOSPath": "El cliente de descarga local {0} informó de la existencia de archivos en {1}, pero no es una ruta válida {2}. Revise la configuración de su cliente de descarga.",
|
"RemotePathMappingCheckFilesLocalWrongOSPath": "El cliente de descarga local {0} informó de la existencia de archivos en {1}, pero no es una ruta válida {2}. Revise la configuración de su cliente de descarga.",
|
||||||
"RemoveSelectedItem": "Eliminar el elemento seleccionado",
|
"RemoveSelectedItem": "Eliminar el elemento seleccionado",
|
||||||
"RemoveSelectedItems": "Eliminar los elementos seleccionados",
|
"RemoveSelectedItems": "Eliminar los elementos seleccionados",
|
||||||
"SelectReleaseGroup": "Seleccionar el grupo de lanzamiento",
|
"SelectReleaseGroup": "Seleccionar Grupo De Lanzamiento",
|
||||||
"SetReleaseGroup": "Configurar el grupo de lanzamiento",
|
"SetReleaseGroup": "Configurar el grupo de lanzamiento",
|
||||||
"TaskUserAgentTooltip": "User-Agent proporcionado por la aplicación llamó a la API",
|
"TaskUserAgentTooltip": "User-Agent proporcionado por la aplicación llamó a la API",
|
||||||
"RssSyncHelpText": "Intervalo en minutos. Ajustar a cero para inhabilitar (esto dentendrá toda captura de estrenos automática)",
|
"RssSyncHelpText": "Intervalo en minutos. Ajustar a cero para inhabilitar (esto dentendrá toda captura de estrenos automática)",
|
||||||
@@ -1119,5 +1119,28 @@
|
|||||||
"ChooseImportMode": "Elegir Modo de Importación",
|
"ChooseImportMode": "Elegir Modo de Importación",
|
||||||
"CollectionOptions": "Opciones de la Colección",
|
"CollectionOptions": "Opciones de la Colección",
|
||||||
"CollectionShowDetailsHelpText": "Mostrar el estado y propiedades de la colección",
|
"CollectionShowDetailsHelpText": "Mostrar el estado y propiedades de la colección",
|
||||||
"CollectionShowOverviewsHelpText": "Mostrar resumenes de la colección"
|
"CollectionShowOverviewsHelpText": "Mostrar resumenes de la colección",
|
||||||
|
"CollectionShowPostersHelpText": "Mostrar póster de artículos de colección",
|
||||||
|
"RefreshMonitoredIntervalHelpText": "Cada cuánto actualizar las descargas monitoreadeas desde los clientes de descarga, mínimo 1 minuto",
|
||||||
|
"SearchOnAddCollectionHelpText": "Buscar películas en esta colección cuando se añada a la biblioteca",
|
||||||
|
"TotalMovies": "Películas Totales",
|
||||||
|
"CollectionsSelectedInterp": "{0} Colecciones Seleccionadas",
|
||||||
|
"EditCollection": "Editar Colección",
|
||||||
|
"MonitorCollection": "Monitorear Colección",
|
||||||
|
"MonitoredCollectionHelpText": "Monitorear para que las películas de esta colección se añadan automáticamente a la biblioteca",
|
||||||
|
"MovieAndCollection": "Película y Colección",
|
||||||
|
"MovieCollectionMultipleMissingRoots": "Múltiples carpetas raices faltantes para colecciones de películas: {0}",
|
||||||
|
"OnMovieAdded": "Al Agregar Pelicula",
|
||||||
|
"OnMovieAddedHelpText": "Al Agregar Película",
|
||||||
|
"RefreshCollections": "Actualizar Colecciones",
|
||||||
|
"ShowCollectionDetails": "Mostrar Estado De Colección",
|
||||||
|
"InstanceName": "Nombre de Instancia",
|
||||||
|
"InstanceNameHelpText": "Nombre de instancia en pestaña y para nombre de aplicación en Syslog",
|
||||||
|
"RottenTomatoesRating": "Tomato Rating",
|
||||||
|
"ScrollMovies": "Desplazar Películas",
|
||||||
|
"ShowPosters": "Mostrar Posters",
|
||||||
|
"UnableToLoadCollections": "No se han podido cargar las colecciones",
|
||||||
|
"MovieCollectionMissingRoot": "Carpeta raíz faltante para colección de películas: {0}",
|
||||||
|
"MovieOnly": "Solo Película",
|
||||||
|
"Database": "Base de Datos"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"RadarrSupportsAnyDownloadClient": "Radarr tukee kaikkien Newznab-yhteensopivien lataustyökalujen ohella myös monia muita alla listattuja torrent- ja Usenet-lataustyökaluja.",
|
"RadarrSupportsAnyDownloadClient": "Radarr tukee kaikkien Newznab-yhteensopivien lataustyökalujen ohella myös monia muita alla listattuja torrent- ja Usenet-lataustyökaluja.",
|
||||||
"Timeleft": "Aikaa jäljellä",
|
"Timeleft": "Aikaa jäljellä",
|
||||||
"UpdateCheckStartupNotWritableMessage": "Päivitystä ei voi asentaa, koska käyttäjällä '{1}' ei ole kirjoitusoikeutta käynnistyskansioon '{0}'.",
|
"UpdateCheckStartupNotWritableMessage": "Päivitystä ei voi asentaa, koska käyttäjällä '{1}' ei ole kirjoitusoikeutta käynnistyskansioon '{0}'.",
|
||||||
"ApplyTagsHelpTexts4": "– 'Korvaa' kaikki aiemmat tunnisteet tai poista kaikki tunnisteet jättämällä tyhjäksi",
|
"ApplyTagsHelpTexts4": "- \"Korvaa\" nykyiset tunnisteet syötetyillä tai tyhjennä kaikki tunnisteet jättämällä tyhjäksi.",
|
||||||
"UnableToLoadUISettings": "Käyttöliittymän asetuksien lataus epäonnistui.",
|
"UnableToLoadUISettings": "Käyttöliittymän asetuksien lataus epäonnistui.",
|
||||||
"NotAvailable": "Ei saatavilla",
|
"NotAvailable": "Ei saatavilla",
|
||||||
"AddingTag": "Tunniste lisätään",
|
"AddingTag": "Tunniste lisätään",
|
||||||
@@ -92,9 +92,9 @@
|
|||||||
"DBMigration": "Tietokannan siirto",
|
"DBMigration": "Tietokannan siirto",
|
||||||
"ApiKey": "API-avain",
|
"ApiKey": "API-avain",
|
||||||
"AcceptConfirmationModal": "Hyväksy vahvistus",
|
"AcceptConfirmationModal": "Hyväksy vahvistus",
|
||||||
"ApplyTagsHelpTexts1": "Miten tunnisteita sovelletaan valittuihin elokuviin",
|
"ApplyTagsHelpTexts1": "Tunnistetoimenpiteiden selitykset:",
|
||||||
"ApplyTagsHelpTexts2": "– 'Lisää' syötetyt tunnisteet aiempiin tunnisteisiin",
|
"ApplyTagsHelpTexts2": "- \"Lisää\" syötetyt tunnisteet aiempiin tunnisteisiin.",
|
||||||
"ApplyTagsHelpTexts3": "– 'Poista' ainoastaan syötetyt tunnisteet",
|
"ApplyTagsHelpTexts3": "- \"Poista\" tyhjentää syötetyt tunnisteet.",
|
||||||
"AvailabilityDelay": "Saatavuusviive",
|
"AvailabilityDelay": "Saatavuusviive",
|
||||||
"AvailabilityDelayHelpText": "Aika ennen käytettävissä olevaa päivämäärää tai sen jälkeen elokuvan etsimiseen",
|
"AvailabilityDelayHelpText": "Aika ennen käytettävissä olevaa päivämäärää tai sen jälkeen elokuvan etsimiseen",
|
||||||
"BackupIntervalHelpText": "Prowlarrin tietokannan ja asetusten automaattisen varmuuskopioinnin suoritusaikaväli.",
|
"BackupIntervalHelpText": "Prowlarrin tietokannan ja asetusten automaattisen varmuuskopioinnin suoritusaikaväli.",
|
||||||
@@ -160,16 +160,16 @@
|
|||||||
"AddToDownloadQueue": "Lisää latausjonoon",
|
"AddToDownloadQueue": "Lisää latausjonoon",
|
||||||
"AfterManualRefresh": "Manuaalisen päivityksen jälkeen",
|
"AfterManualRefresh": "Manuaalisen päivityksen jälkeen",
|
||||||
"AllFiles": "Kaikki tiedostot",
|
"AllFiles": "Kaikki tiedostot",
|
||||||
"AllMoviesInPathHaveBeenImported": "Kaikki kansion '{0}' elokuvat on tuotu",
|
"AllMoviesInPathHaveBeenImported": "Kaikki kansion {0} elokuvat on tuotu",
|
||||||
"AllResultsHiddenFilter": "Aktiivinen suodatin on piilottanut kaikki tulokset",
|
"AllResultsHiddenFilter": "Kaikki tulokset on piilotettu aktiivisen suodattimen johdosta.",
|
||||||
"Always": "Aina",
|
"Always": "Aina",
|
||||||
"AnalyseVideoFiles": "Analysoi videotiedostot",
|
"AnalyseVideoFiles": "Analysoi videotiedostot",
|
||||||
"AnalyticsEnabledHelpText": "Lähetä nimettömiä käyttö- ja virhetietoja sovelluksen palvelimille. Tämä sisältää tietoja selaimestasi, verkkokäyttöliittymän sivujen käytöstä, virheraportoinnista sekä käyttöjärjestelmästäsi ja versiosta. Käytämme näitä tietoja ominaisuuksien ja virhekorjauksien painotukseen.",
|
"AnalyticsEnabledHelpText": "Lähetä nimettömiä käyttö- ja virhetietoja sovelluksen palvelimille. Tämä sisältää tietoja selaimestasi, verkkokäyttöliittymän sivujen käytöstä, virheraportoinnista sekä käyttöjärjestelmästäsi ja versiosta. Käytämme näitä tietoja ominaisuuksien ja virhekorjauksien painotukseen.",
|
||||||
"AptUpdater": "Käytä APT:ta päivityksen asennukseen",
|
"AptUpdater": "Käytä APT:ta päivityksen asennukseen",
|
||||||
"AreYouSureYouWantToDeleteFormat": "Haluatko varmasti poistaa muotoilutagin '{0}'?",
|
"AreYouSureYouWantToDeleteFormat": "Haluatko varmasti poistaa muotoilutunnisteen \"{0}\"?",
|
||||||
"AreYouSureYouWantToDeleteThisImportListExclusion": "Haluatko varmasti poistaa poikkeussäännön?",
|
"AreYouSureYouWantToDeleteThisImportListExclusion": "Haluatko varmasti poistaa tämän poikkeussäännön?",
|
||||||
"AreYouSureYouWantToRemoveSelectedItemsFromQueue": "Haluatko varmasti poistaa jonosta {0} kohdetta?",
|
"AreYouSureYouWantToRemoveSelectedItemsFromQueue": "Haluatko varmasti poistaa jonosta {0} kohdetta?",
|
||||||
"ApplyTags": "Toimenpide tunnisteille",
|
"ApplyTags": "Tunnistetoimenpide",
|
||||||
"AuthBasic": "Perus (selaimen ponnahdus)",
|
"AuthBasic": "Perus (selaimen ponnahdus)",
|
||||||
"AuthenticationMethodHelpText": "Vaadi käyttäjätunnus ja salasana.",
|
"AuthenticationMethodHelpText": "Vaadi käyttäjätunnus ja salasana.",
|
||||||
"AuthForm": "Lomake (kirjautumissivu)",
|
"AuthForm": "Lomake (kirjautumissivu)",
|
||||||
@@ -253,7 +253,7 @@
|
|||||||
"ProxyType": "Välityspalvelimen tyyppi",
|
"ProxyType": "Välityspalvelimen tyyppi",
|
||||||
"PtpOldSettingsCheckMessage": "Seuraavat PassThePopcorn-tietolähteet sisältävät vanhentuneita asetuksia, jotka olisi syytä päivittää: {0}",
|
"PtpOldSettingsCheckMessage": "Seuraavat PassThePopcorn-tietolähteet sisältävät vanhentuneita asetuksia, jotka olisi syytä päivittää: {0}",
|
||||||
"QualitiesHelpText": "Listalla ylempänä olevia laatuja painotetaan. Saman ryhmän laadut ovat tasaveroisia. Valitse vain haluamasi laadut.",
|
"QualitiesHelpText": "Listalla ylempänä olevia laatuja painotetaan. Saman ryhmän laadut ovat tasaveroisia. Valitse vain haluamasi laadut.",
|
||||||
"QualityProfileInUse": "Elokuvaan liitettyä laatuprofiilia ei voi poistaa.",
|
"QualityProfileInUse": "Elokuvaan, listaan tai kokelmaan liitettyä laatuprofiilia ei voida poistaa.",
|
||||||
"QueueIsEmpty": "Jono on tyhjä",
|
"QueueIsEmpty": "Jono on tyhjä",
|
||||||
"RadarrCalendarFeed": "Radarr-kalenterisyöte",
|
"RadarrCalendarFeed": "Radarr-kalenterisyöte",
|
||||||
"RadarrUpdated": "Radarr Päivitetty",
|
"RadarrUpdated": "Radarr Päivitetty",
|
||||||
@@ -397,7 +397,7 @@
|
|||||||
"Path": "Sijainti",
|
"Path": "Sijainti",
|
||||||
"Paused": "Keskeytetty",
|
"Paused": "Keskeytetty",
|
||||||
"Permissions": "Käyttöoikeudet",
|
"Permissions": "Käyttöoikeudet",
|
||||||
"AllMoviesHiddenDueToFilter": "Käytössä oleva suodatin on piilottanut kaikki elokuvat.",
|
"AllMoviesHiddenDueToFilter": "Kaikki elokuvat on piilotettu aktiivisen suodattimen johdosta.",
|
||||||
"AllowHardcodedSubs": "Salli poltetut tekstitykset",
|
"AllowHardcodedSubs": "Salli poltetut tekstitykset",
|
||||||
"PhysicalRelease": "Fyysinen julkaisu",
|
"PhysicalRelease": "Fyysinen julkaisu",
|
||||||
"Port": "Portti",
|
"Port": "Portti",
|
||||||
@@ -409,7 +409,7 @@
|
|||||||
"ProtocolHelpText": "Valitse käytettävä (t) protokolla (t) ja mikä on suositeltava, kun valitset muuten yhtä suurten julkaisujen välillä",
|
"ProtocolHelpText": "Valitse käytettävä (t) protokolla (t) ja mikä on suositeltava, kun valitset muuten yhtä suurten julkaisujen välillä",
|
||||||
"EditDelayProfile": "Muokkaa viiveprofiilia",
|
"EditDelayProfile": "Muokkaa viiveprofiilia",
|
||||||
"ProxyBypassFilterHelpText": "Käytä erottimena ',' ja '*.' jokerimerkkinä aliverkkotunnuksille (esim. www.esimerkki.fi,*.esimerkki.fi)",
|
"ProxyBypassFilterHelpText": "Käytä erottimena ',' ja '*.' jokerimerkkinä aliverkkotunnuksille (esim. www.esimerkki.fi,*.esimerkki.fi)",
|
||||||
"AllowHardcodedSubsHelpText": "Havaitut poltetut tekstitykset ladataan automaattisesti",
|
"AllowHardcodedSubsHelpText": "Havaitut poltetut tekstitykset ladataan automaattisesti.",
|
||||||
"QualityProfile": "Laatuprofiili",
|
"QualityProfile": "Laatuprofiili",
|
||||||
"QualityProfiles": "Laatuprofiilit",
|
"QualityProfiles": "Laatuprofiilit",
|
||||||
"QuickImport": "Siirrä automaattisesti",
|
"QuickImport": "Siirrä automaattisesti",
|
||||||
@@ -443,7 +443,7 @@
|
|||||||
"TotalFileSize": "Tiedoston koko",
|
"TotalFileSize": "Tiedoston koko",
|
||||||
"TotalSpace": "Kokonaistila",
|
"TotalSpace": "Kokonaistila",
|
||||||
"UpgradeAllowedHelpText": "Jos käytöstä poistettuja laatuja ei päivitetä.",
|
"UpgradeAllowedHelpText": "Jos käytöstä poistettuja laatuja ei päivitetä.",
|
||||||
"AreYouSureYouWantToDeleteThisDelayProfile": "Haluatko varmasti poistaa viiveprofiilin?",
|
"AreYouSureYouWantToDeleteThisDelayProfile": "Haluatko varmasti poistaa tämän viiveprofiilin?",
|
||||||
"Username": "Käyttäjätunnus",
|
"Username": "Käyttäjätunnus",
|
||||||
"WaitingToImport": "Odottaa tuontia",
|
"WaitingToImport": "Odottaa tuontia",
|
||||||
"DigitalRelease": "Digitaalinen julkaisu",
|
"DigitalRelease": "Digitaalinen julkaisu",
|
||||||
@@ -473,8 +473,8 @@
|
|||||||
"ConnectionLost": "Yhteys on katkennut",
|
"ConnectionLost": "Yhteys on katkennut",
|
||||||
"Backup": "Varmuuskopio",
|
"Backup": "Varmuuskopio",
|
||||||
"ConnectionLostAutomaticMessage": "Radarr pyrkii muodostamaan yhteyden automaattisesti tai voit painaa alta \"Lataa uudelleen\".",
|
"ConnectionLostAutomaticMessage": "Radarr pyrkii muodostamaan yhteyden automaattisesti tai voit painaa alta \"Lataa uudelleen\".",
|
||||||
"AddNewMessage": "On helppoa lisätä uusi elokuva: ala vain kirjoittamaan haluamasi elokuvan nimeä.",
|
"AddNewMessage": "Uuden elokuvan lisäys on helppoa. Aloita vain haluamasi elokuvan nimen kirjoitus.",
|
||||||
"AddNewTmdbIdMessage": "Voit tehdä hakuja myös elokuvan TMDb-tunnuksella (esim. 'tmdb:71663').",
|
"AddNewTmdbIdMessage": "Voit etsiä myös elokuvan TMDb-tunnisteella (esim. 'tmdb:71663').",
|
||||||
"BackupFolderHelpText": "Suhteelliset polut kohdistuvat sovelluksen AppData-kansioon.",
|
"BackupFolderHelpText": "Suhteelliset polut kohdistuvat sovelluksen AppData-kansioon.",
|
||||||
"Connections": "Kytkennät",
|
"Connections": "Kytkennät",
|
||||||
"ConnectSettings": "Kytkentöjen asetukset",
|
"ConnectSettings": "Kytkentöjen asetukset",
|
||||||
@@ -787,7 +787,7 @@
|
|||||||
"RenameMovies": "Uudelleennimeä elokuvat",
|
"RenameMovies": "Uudelleennimeä elokuvat",
|
||||||
"RenameMoviesHelpText": "Jos uudelleennimeäminen ei ole käytössä, käytetään olemassa olevaa tiedostonimeä.",
|
"RenameMoviesHelpText": "Jos uudelleennimeäminen ei ole käytössä, käytetään olemassa olevaa tiedostonimeä.",
|
||||||
"Reorder": "Järjestä uudelleen",
|
"Reorder": "Järjestä uudelleen",
|
||||||
"Replace": "Korvata",
|
"Replace": "Korvaa",
|
||||||
"ReplaceIllegalCharacters": "Korvaa kielletyt merkit",
|
"ReplaceIllegalCharacters": "Korvaa kielletyt merkit",
|
||||||
"ReplaceIllegalCharactersHelpText": "Korvaa laittomat merkit. Jos ei käytössä, laittomat merkit poistetaan.",
|
"ReplaceIllegalCharactersHelpText": "Korvaa laittomat merkit. Jos ei käytössä, laittomat merkit poistetaan.",
|
||||||
"ReplaceWithSpaceDashSpace": "Korvus: välilyönti-väliviiva-välilyönti",
|
"ReplaceWithSpaceDashSpace": "Korvus: välilyönti-väliviiva-välilyönti",
|
||||||
@@ -1116,7 +1116,7 @@
|
|||||||
"RssSyncHelpText": "Aikaväli minuutteina. Arvo nolla kytkee toiminnon pois käytöstä ja lopettaen samalla automaattisen julkaisujen kaappauksen täysin.",
|
"RssSyncHelpText": "Aikaväli minuutteina. Arvo nolla kytkee toiminnon pois käytöstä ja lopettaen samalla automaattisen julkaisujen kaappauksen täysin.",
|
||||||
"InstanceName": "Instanssin nimi",
|
"InstanceName": "Instanssin nimi",
|
||||||
"InstanceNameHelpText": "Instanssin nimi välilehdellä ja järjestelmälokissa",
|
"InstanceNameHelpText": "Instanssin nimi välilehdellä ja järjestelmälokissa",
|
||||||
"AllCollectionsHiddenDueToFilter": "Käytössä oleva suodatin on piilottanut kaikki kokoelmat.",
|
"AllCollectionsHiddenDueToFilter": "Kaikki kokoelmat on piilotettu aktiivisen suodattimen johdosta.",
|
||||||
"Collections": "Kokoelmat",
|
"Collections": "Kokoelmat",
|
||||||
"MonitorMovies": "Valvo elokuvia",
|
"MonitorMovies": "Valvo elokuvia",
|
||||||
"NoCollections": "Kokoelmia ei löytynyt. Aloita lisäämällä uusi elokuva tai tuo joitakin olemassa olevia.",
|
"NoCollections": "Kokoelmia ei löytynyt. Aloita lisäämällä uusi elokuva tai tuo joitakin olemassa olevia.",
|
||||||
@@ -1142,5 +1142,8 @@
|
|||||||
"CollectionOptions": "Kokoelmien valinnat",
|
"CollectionOptions": "Kokoelmien valinnat",
|
||||||
"CollectionShowDetailsHelpText": "Näytä kokoelmien tila ja ominaisuudet",
|
"CollectionShowDetailsHelpText": "Näytä kokoelmien tila ja ominaisuudet",
|
||||||
"CollectionShowPostersHelpText": "Näytä kokoelmien julisteet",
|
"CollectionShowPostersHelpText": "Näytä kokoelmien julisteet",
|
||||||
"RottenTomatoesRating": "Tomaattiarvio"
|
"RottenTomatoesRating": "Tomaattiarvio",
|
||||||
|
"TotalMovies": "Elokuvia yhteensä",
|
||||||
|
"ApplicationURL": "Sovelluksen URL-osoite",
|
||||||
|
"ApplicationUrlHelpText": "Sovelluksen ulkoinen URL-osoite, johon sisältyy http(s)://, portti ja URL-perusta."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@
|
|||||||
"RemotePathMappings": "Mappages de chemins distants",
|
"RemotePathMappings": "Mappages de chemins distants",
|
||||||
"ReleaseBranchCheckOfficialBranchMessage": "La branche {0} n'est pas une branche de version Radarr valide, vous ne recevrez pas de mises à jour",
|
"ReleaseBranchCheckOfficialBranchMessage": "La branche {0} n'est pas une branche de version Radarr valide, vous ne recevrez pas de mises à jour",
|
||||||
"RefreshAndScan": "Actualiser et analyser",
|
"RefreshAndScan": "Actualiser et analyser",
|
||||||
"Refresh": "Rafraîchir",
|
"Refresh": "Actualiser",
|
||||||
"Ratings": "Évaluations",
|
"Ratings": "Évaluations",
|
||||||
"Queue": "File d'attente",
|
"Queue": "File d'attente",
|
||||||
"QualitySettingsSummary": "Tailles qualité et dénomination",
|
"QualitySettingsSummary": "Tailles qualité et dénomination",
|
||||||
@@ -1114,7 +1114,7 @@
|
|||||||
"SetReleaseGroup": "Régler le groupe de publication",
|
"SetReleaseGroup": "Régler le groupe de publication",
|
||||||
"RefreshMonitoredIntervalHelpText": "Intervalle en minutes entre chaque vérification des téléchargements, minimum 1 minute",
|
"RefreshMonitoredIntervalHelpText": "Intervalle en minutes entre chaque vérification des téléchargements, minimum 1 minute",
|
||||||
"AllCollectionsHiddenDueToFilter": "Tous les films sont masqués en raison du filtre appliqué.",
|
"AllCollectionsHiddenDueToFilter": "Tous les films sont masqués en raison du filtre appliqué.",
|
||||||
"Collections": "Collection",
|
"Collections": "Collections",
|
||||||
"MonitorMovies": "Surveiller le film",
|
"MonitorMovies": "Surveiller le film",
|
||||||
"NoCollections": "Aucun film trouvé, pour commencer, vous voudrez ajouter un nouveau film ou importer des films existants.",
|
"NoCollections": "Aucun film trouvé, pour commencer, vous voudrez ajouter un nouveau film ou importer des films existants.",
|
||||||
"RssSyncHelpText": "Intervalle en minutes. Mettre à zéro pour désactiver (cela arrêtera tous les téléchargements automatiques)",
|
"RssSyncHelpText": "Intervalle en minutes. Mettre à zéro pour désactiver (cela arrêtera tous les téléchargements automatiques)",
|
||||||
@@ -1122,5 +1122,25 @@
|
|||||||
"ChooseImportMode": "Mode d'importation",
|
"ChooseImportMode": "Mode d'importation",
|
||||||
"CollectionOptions": "Options de collection",
|
"CollectionOptions": "Options de collection",
|
||||||
"CollectionShowDetailsHelpText": "Afficher l'état et les propriétés de la collection",
|
"CollectionShowDetailsHelpText": "Afficher l'état et les propriétés de la collection",
|
||||||
"CollectionShowOverviewsHelpText": "Afficher les aperçus des collections"
|
"CollectionShowOverviewsHelpText": "Afficher les aperçus des collections",
|
||||||
|
"OnMovieAddedHelpText": "À l'ajout d'un film",
|
||||||
|
"MovieAndCollection": "Film et collection",
|
||||||
|
"ShowOverview": "Afficher l'aperçu",
|
||||||
|
"MovieCollectionMissingRoot": "Dossier racine manquant pour la collection de films : {0}",
|
||||||
|
"OnMovieAdded": "À l'ajout d'un film",
|
||||||
|
"MonitorCollection": "Surveiller la collection",
|
||||||
|
"MonitoredCollectionHelpText": "Surveiller pour ajouter automatiquement les films de cette collection à la bibliothèque",
|
||||||
|
"MovieCollectionMultipleMissingRoots": "Plusieurs dossiers racine manquent pour les collections de films: {0}",
|
||||||
|
"MovieOnly": "Film seulement",
|
||||||
|
"RefreshCollections": "Actualiser les collections",
|
||||||
|
"SearchOnAddCollectionHelpText": "Rechercher des films dans cette collection lorsqu'ils sont ajoutés à Radarr",
|
||||||
|
"UnableToLoadCollections": "Impossible de charger les collections",
|
||||||
|
"RottenTomatoesRating": "Classement Rotten Tomatoes",
|
||||||
|
"ShowCollectionDetails": "Afficher l'état de la collection",
|
||||||
|
"EditCollection": "Modifier la collection",
|
||||||
|
"ApplicationURL": "URL de l'application",
|
||||||
|
"ApplicationUrlHelpText": "URL externe de cette application, y compris http(s)://, le port et l'URL de base",
|
||||||
|
"InstanceName": "Nom de l'instance",
|
||||||
|
"InstanceNameHelpText": "Nom de l'instance dans l'onglet du navigateur et pour le nom d'application dans Syslog",
|
||||||
|
"CollectionShowPostersHelpText": "Afficher les affiches des éléments de la collection"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"AddExclusion": "Kivétel hozzáadása",
|
"AddExclusion": "Kivétel hozzáadása",
|
||||||
"Activity": "Aktivitás",
|
"Activity": "Aktivitás",
|
||||||
"Error": "Hiba",
|
"Error": "Hiba",
|
||||||
"Ended": "Befejezve",
|
"Ended": "Vége lett",
|
||||||
"EnableCompletedDownloadHandlingHelpText": "A befejezett letöltések automatikus importálása a letöltési kliensből",
|
"EnableCompletedDownloadHandlingHelpText": "A befejezett letöltések automatikus importálása a letöltési kliensből",
|
||||||
"EnableColorImpairedModeHelpText": "Megváltoztatott színek, hogy a színvak felhasználók jobban meg tudják különböztetni a színkódolt információkat",
|
"EnableColorImpairedModeHelpText": "Megváltoztatott színek, hogy a színvak felhasználók jobban meg tudják különböztetni a színkódolt információkat",
|
||||||
"EnableAutomaticSearchHelpTextWarning": "Interaktív keresés esetén is felhasználható",
|
"EnableAutomaticSearchHelpTextWarning": "Interaktív keresés esetén is felhasználható",
|
||||||
@@ -434,7 +434,7 @@
|
|||||||
"RadarrSupportsAnyIndexer": "A Radarr minden indexert támogat, amely a Newznab szabványt használja, valamint az alább felsorolt egyéb indexereket.",
|
"RadarrSupportsAnyIndexer": "A Radarr minden indexert támogat, amely a Newznab szabványt használja, valamint az alább felsorolt egyéb indexereket.",
|
||||||
"RadarrSupportsAnyDownloadClient": "A Radarr számos népszerű torrent és usenet letöltési ügyfelet támogat.",
|
"RadarrSupportsAnyDownloadClient": "A Radarr számos népszerű torrent és usenet letöltési ügyfelet támogat.",
|
||||||
"QuickImport": "Automatikus Áthelyezés",
|
"QuickImport": "Automatikus Áthelyezés",
|
||||||
"Queued": "Sorban",
|
"Queued": "Sorba helyezve",
|
||||||
"Queue": "Várakozási sor",
|
"Queue": "Várakozási sor",
|
||||||
"QualitySettingsSummary": "Minőségi méretek és elnevezés",
|
"QualitySettingsSummary": "Minőségi méretek és elnevezés",
|
||||||
"QualitySettings": "Minőségi beállítások",
|
"QualitySettings": "Minőségi beállítások",
|
||||||
@@ -616,7 +616,7 @@
|
|||||||
"Level": "Szint",
|
"Level": "Szint",
|
||||||
"LaunchBrowserHelpText": " Nyisson meg egy böngészőt, és az alkalmazás indításakor lépjen a Radarr kezdőlapjára.",
|
"LaunchBrowserHelpText": " Nyisson meg egy böngészőt, és az alkalmazás indításakor lépjen a Radarr kezdőlapjára.",
|
||||||
"LastWriteTime": "Utolsó írási idő",
|
"LastWriteTime": "Utolsó írási idő",
|
||||||
"LastDuration": "Utolsó Hossza",
|
"LastDuration": "Utolsó időtartam",
|
||||||
"Languages": "Nyelvek",
|
"Languages": "Nyelvek",
|
||||||
"LanguageHelpText": "Nyelv a Releasekhez",
|
"LanguageHelpText": "Nyelv a Releasekhez",
|
||||||
"Language": "Nyelv",
|
"Language": "Nyelv",
|
||||||
@@ -896,7 +896,7 @@
|
|||||||
"HomePage": "Kezdőlap",
|
"HomePage": "Kezdőlap",
|
||||||
"LogOnly": "Csak naplózás",
|
"LogOnly": "Csak naplózás",
|
||||||
"LastUsed": "Utoljára használt",
|
"LastUsed": "Utoljára használt",
|
||||||
"LastExecution": "Utoljára végrehajtott",
|
"LastExecution": "Utolsó végrehajtás",
|
||||||
"Large": "Óriási",
|
"Large": "Óriási",
|
||||||
"KeepAndUnmonitorMovie": "Megtartás és megfigyelés kikapcsolása a filmnél",
|
"KeepAndUnmonitorMovie": "Megtartás és megfigyelés kikapcsolása a filmnél",
|
||||||
"InvalidFormat": "Érvénytelen Formátum",
|
"InvalidFormat": "Érvénytelen Formátum",
|
||||||
@@ -963,7 +963,7 @@
|
|||||||
"RadarrUpdated": "A Radarr frissítve",
|
"RadarrUpdated": "A Radarr frissítve",
|
||||||
"RadarrCalendarFeed": "Radarr Naptár Feed",
|
"RadarrCalendarFeed": "Radarr Naptár Feed",
|
||||||
"QueueIsEmpty": "A várakozási sor üres",
|
"QueueIsEmpty": "A várakozási sor üres",
|
||||||
"QualityProfileInUse": "A minőség profil ami már filmhez van rendelve, nem törölhető",
|
"QualityProfileInUse": "A filmhez, listához vagy gyűjteményhez csatolt minőségi profil nem törölhető",
|
||||||
"QualityLimitsHelpText": "A korlátozások automatikusan beállítódnak a film hossza szerint.",
|
"QualityLimitsHelpText": "A korlátozások automatikusan beállítódnak a film hossza szerint.",
|
||||||
"QualitiesHelpText": "A nagyobb minőség a listában jobban preferált. Ugyenazon minőségek a csoportban egyenlőek. Csak a bejelölt minőségek a kívánt minőségek",
|
"QualitiesHelpText": "A nagyobb minőség a listában jobban preferált. Ugyenazon minőségek a csoportban egyenlőek. Csak a bejelölt minőségek a kívánt minőségek",
|
||||||
"Qualities": "Minőségek",
|
"Qualities": "Minőségek",
|
||||||
@@ -992,7 +992,7 @@
|
|||||||
"NoLinks": "Nincsenek Linkek",
|
"NoLinks": "Nincsenek Linkek",
|
||||||
"NoEventsFound": "Nem található esemény",
|
"NoEventsFound": "Nem található esemény",
|
||||||
"NoAltTitle": "Nincs alternatív cím.",
|
"NoAltTitle": "Nincs alternatív cím.",
|
||||||
"NextExecution": "Következő futtatás",
|
"NextExecution": "Következő végrehajtás",
|
||||||
"Negated": "Megtagadva",
|
"Negated": "Megtagadva",
|
||||||
"Negate": "Megtagadás",
|
"Negate": "Megtagadás",
|
||||||
"MultiLanguage": "Többnyelvű",
|
"MultiLanguage": "Többnyelvű",
|
||||||
@@ -1105,7 +1105,7 @@
|
|||||||
"Never": "Soha",
|
"Never": "Soha",
|
||||||
"Rating": "Értékelés",
|
"Rating": "Értékelés",
|
||||||
"SetReleaseGroup": "Kiadási csoport beállítása",
|
"SetReleaseGroup": "Kiadási csoport beállítása",
|
||||||
"Started": "Elindult",
|
"Started": "Elkezdődött",
|
||||||
"Waiting": "Várakozás",
|
"Waiting": "Várakozás",
|
||||||
"OnApplicationUpdateHelpText": "Alkalmazásfrissítésről",
|
"OnApplicationUpdateHelpText": "Alkalmazásfrissítésről",
|
||||||
"SizeLimit": "Méretkorlát",
|
"SizeLimit": "Méretkorlát",
|
||||||
@@ -1142,5 +1142,8 @@
|
|||||||
"OnMovieAddedHelpText": "Film hozzáadásakor",
|
"OnMovieAddedHelpText": "Film hozzáadásakor",
|
||||||
"ShowPosters": "Poszterek megjelenítése",
|
"ShowPosters": "Poszterek megjelenítése",
|
||||||
"InstanceNameHelpText": "Példánynév a böngésző lapon és a syslog alkalmazás neve",
|
"InstanceNameHelpText": "Példánynév a böngésző lapon és a syslog alkalmazás neve",
|
||||||
"RottenTomatoesRating": "Tomato Értékelés"
|
"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"
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/NzbDrone.Core/Localization/Core/lv.json
Normal file
1
src/NzbDrone.Core/Localization/Core/lv.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
@@ -254,5 +254,6 @@
|
|||||||
"Language": "språk",
|
"Language": "språk",
|
||||||
"List": "Liste",
|
"List": "Liste",
|
||||||
"New": "Ny",
|
"New": "Ny",
|
||||||
"RemotePathMappings": "Ekstern portmapping"
|
"RemotePathMappings": "Ekstern portmapping",
|
||||||
|
"Languages": "språk"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1105,5 +1105,7 @@
|
|||||||
"RssSyncHelpText": "Tussentijd in minuten. Schakel uit met 0 (dit stopt het automatisch ophalen van uitgaves)",
|
"RssSyncHelpText": "Tussentijd in minuten. Schakel uit met 0 (dit stopt het automatisch ophalen van uitgaves)",
|
||||||
"NoCollections": "Geen films gevonden, om te beginnen, voeg een nieuwe film toe of importeer bestaande films.",
|
"NoCollections": "Geen films gevonden, om te beginnen, voeg een nieuwe film toe of importeer bestaande films.",
|
||||||
"Collections": "Collectie",
|
"Collections": "Collectie",
|
||||||
"MonitorMovies": "Bewaak Film"
|
"MonitorMovies": "Bewaak Film",
|
||||||
|
"ApplicationURL": "Applicatie URL",
|
||||||
|
"ApplicationUrlHelpText": "De externe URL van deze applicatie inclusief http(s)://,Port en URL base"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -289,7 +289,7 @@
|
|||||||
"Presets": "Presety",
|
"Presets": "Presety",
|
||||||
"ProxyType": "Typ proxy",
|
"ProxyType": "Typ proxy",
|
||||||
"PtpOldSettingsCheckMessage": "Następujące indeksatory PassThePopcorn mają przestarzałe ustawienia i należy je zaktualizować: {0}",
|
"PtpOldSettingsCheckMessage": "Następujące indeksatory PassThePopcorn mają przestarzałe ustawienia i należy je zaktualizować: {0}",
|
||||||
"QualityProfileInUse": "Nie można usunąć profilu jakości dołączonego do filmu",
|
"QualityProfileInUse": "Nie można usunąć profilu jakości przypisanego do filmu, listy bądź kolekcji",
|
||||||
"QueueIsEmpty": "Kolejka jest pusta",
|
"QueueIsEmpty": "Kolejka jest pusta",
|
||||||
"RadarrCalendarFeed": "Kanał kalendarza radarowego",
|
"RadarrCalendarFeed": "Kanał kalendarza radarowego",
|
||||||
"RadarrUpdated": "Radarr zaktualizowany",
|
"RadarrUpdated": "Radarr zaktualizowany",
|
||||||
@@ -1060,9 +1060,9 @@
|
|||||||
"SelectLanguages": "Wybierz język",
|
"SelectLanguages": "Wybierz język",
|
||||||
"AllCollectionsHiddenDueToFilter": "Wszystkie kolekcje są ukryte ze względu na zastosowany filtr.",
|
"AllCollectionsHiddenDueToFilter": "Wszystkie kolekcje są ukryte ze względu na zastosowany filtr.",
|
||||||
"Collections": "Kolekcje",
|
"Collections": "Kolekcje",
|
||||||
"RssSyncHelpText": "Odstęp w minutach. Ustaw na zero, aby wyłączyć (zatrzyma to wszystkie automatyczne przechwytywanie zwolnień)",
|
"RssSyncHelpText": "Odstęp w minutach. Ustaw na zero, aby wyłączyć (zatrzyma to wszystkie automatyczne przechwytywanie)",
|
||||||
"NoCollections": "Nie znaleziono żadnych filmów. Aby rozpocząć, musisz dodać nowy film lub zaimportować istniejące",
|
"NoCollections": "Nie znaleziono żadnych filmów. Aby rozpocząć, musisz dodać nowy film lub zaimportować istniejące",
|
||||||
"MonitorMovies": "Monitoruj film",
|
"MonitorMovies": "Monitoruj filmy",
|
||||||
"ClickToChangeReleaseGroup": "Kliknij, by zmienić grupę wydającą",
|
"ClickToChangeReleaseGroup": "Kliknij, by zmienić grupę wydającą",
|
||||||
"RemotePathMappingCheckDownloadPermissions": "Radarr widzi film {0}, lecz nie ma do niego dostępu. Najprawdopodobniej to wynik błędu w uprawnieniach dostępu.",
|
"RemotePathMappingCheckDownloadPermissions": "Radarr widzi film {0}, lecz nie ma do niego dostępu. Najprawdopodobniej to wynik błędu w uprawnieniach dostępu.",
|
||||||
"NotificationTriggersHelpText": "Wybierz zdarzenia, które mają uruchamiać to powiadomienie",
|
"NotificationTriggersHelpText": "Wybierz zdarzenia, które mają uruchamiać to powiadomienie",
|
||||||
@@ -1078,7 +1078,7 @@
|
|||||||
"ManualImportSetReleaseGroup": "Import ręczny - podaj nazwę grupy",
|
"ManualImportSetReleaseGroup": "Import ręczny - podaj nazwę grupy",
|
||||||
"Never": "Nigdy",
|
"Never": "Nigdy",
|
||||||
"RemotePathMappingCheckFilesWrongOSPath": "Zdalny klient pobierania {0} zgłosił pliki w {1}, lecz nie jest to poprawna ścieżka {2}. Zmień ustawienia zdalnego mapowania ścieżek i klienta pobierania.",
|
"RemotePathMappingCheckFilesWrongOSPath": "Zdalny klient pobierania {0} zgłosił pliki w {1}, lecz nie jest to poprawna ścieżka {2}. Zmień ustawienia zdalnego mapowania ścieżek i klienta pobierania.",
|
||||||
"RemotePathMappingCheckGenericPermissions": "Klient pobierania {0} umieszcza pobrane pliki w {1}, lecz Radarr nie widzi tego folderu. Być może należy zmienić uprawnienia dostępu do folderu.",
|
"RemotePathMappingCheckGenericPermissions": "Klient pobierania {0} umieszcza pobrane pliki w {1}, ale Radarr nie widzi tego folderu. Prawdopodobnie należy zmienić uprawnienia dostępu do folderu.",
|
||||||
"RemoveFailed": "Usuń nieudane",
|
"RemoveFailed": "Usuń nieudane",
|
||||||
"RemoveDownloadsAlert": "Ustawienia usuwania zostały przeniesione do ustawień poszczególnych klientów pobierania powyżej.",
|
"RemoveDownloadsAlert": "Ustawienia usuwania zostały przeniesione do ustawień poszczególnych klientów pobierania powyżej.",
|
||||||
"ShowCollectionDetails": "Pokaż stan kolekcji",
|
"ShowCollectionDetails": "Pokaż stan kolekcji",
|
||||||
@@ -1141,5 +1141,9 @@
|
|||||||
"CollectionShowPostersHelpText": "Pokaż plakaty elementów kolekcji",
|
"CollectionShowPostersHelpText": "Pokaż plakaty elementów kolekcji",
|
||||||
"CollectionOptions": "Opcje Kolekcji",
|
"CollectionOptions": "Opcje Kolekcji",
|
||||||
"CollectionShowDetailsHelpText": "Pokaż status i właściwości kolekcji",
|
"CollectionShowDetailsHelpText": "Pokaż status i właściwości kolekcji",
|
||||||
"CollectionShowOverviewsHelpText": "Pokaż przegląd kolekcji"
|
"CollectionShowOverviewsHelpText": "Pokaż przegląd kolekcji",
|
||||||
|
"TotalMovies": "Filmów całkowicie",
|
||||||
|
"RottenTomatoesRating": "Ocena Tomato",
|
||||||
|
"ApplicationUrlHelpText": "Zewnętrzny URL tej aplikacji zawiera http(s)://, port i adres URL",
|
||||||
|
"ApplicationURL": "Link do aplikacji"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,7 +194,7 @@
|
|||||||
"IndexerStatusCheckSingleClientMessage": "Indexadores indisponíveis devido a falhas: {0}",
|
"IndexerStatusCheckSingleClientMessage": "Indexadores indisponíveis devido a falhas: {0}",
|
||||||
"IndexerStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a falhas",
|
"IndexerStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a falhas",
|
||||||
"IndexersSettingsSummary": "Restrições de versões e de indexadores",
|
"IndexersSettingsSummary": "Restrições de versões e de indexadores",
|
||||||
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa Interativa ativada, o Radarr não fornecerá nenhum resultado das Pesquisas Interativas",
|
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa Interativa ativada, o Radarr não fornecerá nenhum resultado nas Pesquisas Interativas",
|
||||||
"IndexerSearchCheckNoAutomaticMessage": "Não há indexadores disponíveis com Pesquisa automática ativada, o Radarr não fornecerá nenhum resultado de pesquisa automática",
|
"IndexerSearchCheckNoAutomaticMessage": "Não há indexadores disponíveis com Pesquisa automática ativada, o Radarr não fornecerá nenhum resultado de pesquisa automática",
|
||||||
"IndexerSearchCheckNoAvailableIndexersMessage": "Todos os indexadores com funcionalidade de pesquisa estão indisponíveis devido a erros recentes do indexador",
|
"IndexerSearchCheckNoAvailableIndexersMessage": "Todos os indexadores com funcionalidade de pesquisa estão indisponíveis devido a erros recentes do indexador",
|
||||||
"Indexers": "Indexadores",
|
"Indexers": "Indexadores",
|
||||||
@@ -378,7 +378,7 @@
|
|||||||
"MaximumSizeHelpText": "Tamanho máximo de uma versão a capturar, em MB. Defina como zero para Ilimitado",
|
"MaximumSizeHelpText": "Tamanho máximo de uma versão a capturar, em MB. Defina como zero para Ilimitado",
|
||||||
"MaximumSize": "Tamanho máximo",
|
"MaximumSize": "Tamanho máximo",
|
||||||
"MaximumLimits": "Limites máximos",
|
"MaximumLimits": "Limites máximos",
|
||||||
"Logs": "Logs",
|
"Logs": "Registos",
|
||||||
"LogLevel": "Nível de log",
|
"LogLevel": "Nível de log",
|
||||||
"Local": "Local",
|
"Local": "Local",
|
||||||
"ListUpdateInterval": "Intervalo de atualização da lista",
|
"ListUpdateInterval": "Intervalo de atualização da lista",
|
||||||
@@ -650,7 +650,7 @@
|
|||||||
"UpgradeUntilThisQualityIsMetOrExceeded": "Atualizar até que esta qualidade seja alcançada ou superada",
|
"UpgradeUntilThisQualityIsMetOrExceeded": "Atualizar até que esta qualidade seja alcançada ou superada",
|
||||||
"UpgradeAllowedHelpText": "Se desativada, as qualidades não serão atualizadas",
|
"UpgradeAllowedHelpText": "Se desativada, as qualidades não serão atualizadas",
|
||||||
"UpdateScriptPathHelpText": "Caminho para um script personalizado que toma um pacote de atualização extraído e lida com o restante do processo da atualização",
|
"UpdateScriptPathHelpText": "Caminho para um script personalizado que toma um pacote de atualização extraído e lida com o restante do processo da atualização",
|
||||||
"UpdateMechanismHelpText": "Usar o atualizador do Radarr ou um script",
|
"UpdateMechanismHelpText": "Usar o atualizador interno do Radarr ou um script",
|
||||||
"UpdateAutomaticallyHelpText": "Transferir e instalar automaticamente as atualizações. Ainda é possível instalar a partir de Sistema: Atualizações",
|
"UpdateAutomaticallyHelpText": "Transferir e instalar automaticamente as atualizações. Ainda é possível instalar a partir de Sistema: Atualizações",
|
||||||
"UnmonitoredHelpText": "Incluir filmes não monitorados no feed do iCal",
|
"UnmonitoredHelpText": "Incluir filmes não monitorados no feed do iCal",
|
||||||
"TMDBId": "ID do TMDb",
|
"TMDBId": "ID do TMDb",
|
||||||
@@ -684,12 +684,12 @@
|
|||||||
"ShowGenres": "Mostrar géneros",
|
"ShowGenres": "Mostrar géneros",
|
||||||
"ShowCertification": "Mostrar certificação",
|
"ShowCertification": "Mostrar certificação",
|
||||||
"ShowAsAllDayEvents": "Mostrar como eventos de dia inteiro",
|
"ShowAsAllDayEvents": "Mostrar como eventos de dia inteiro",
|
||||||
"ShouldMonitorHelpText": "Se ativada, os filmes desta lista serão adicionados e monitorados",
|
"ShouldMonitorHelpText": "Se filmes ou coleções adicionados por esta lista são adicionados como monitorados",
|
||||||
"SettingsRuntimeFormat": "Formato do tempo de execução",
|
"SettingsRuntimeFormat": "Formato do tempo de execução",
|
||||||
"SetPermissionsLinuxHelpTextWarning": "Se você não conhece essa definição, não a altere.",
|
"SetPermissionsLinuxHelpTextWarning": "Se você não conhece essa definição, não a altere.",
|
||||||
"SetPermissionsLinuxHelpText": "Deve-se executar chmod ao importar/renomear ficheiros?",
|
"SetPermissionsLinuxHelpText": "Deve-se executar chmod ao importar/renomear ficheiros?",
|
||||||
"SendAnonymousUsageData": "Enviar dados anônimos de uso",
|
"SendAnonymousUsageData": "Enviar dados anônimos de uso",
|
||||||
"SearchOnAddHelpText": "Pesquisar os filmes desta lista ao adicionar ao Radarr",
|
"SearchOnAddHelpText": "Pesquisar os filmes desta lista ao adicionar à biblioteca",
|
||||||
"RSSSyncInterval": "Intervalo de sincronização RSS",
|
"RSSSyncInterval": "Intervalo de sincronização RSS",
|
||||||
"RetryingDownloadInterp": "Nova tentativa de transferência {0} em {1}",
|
"RetryingDownloadInterp": "Nova tentativa de transferência {0} em {1}",
|
||||||
"RetentionHelpText": "Somente Usenet: defina como zero para retenção ilimitada",
|
"RetentionHelpText": "Somente Usenet: defina como zero para retenção ilimitada",
|
||||||
@@ -706,11 +706,11 @@
|
|||||||
"RadarrSupportsCustomConditionsAgainstTheReleasePropertiesBelow": "O Radar suporta condições personalizadas em relação às propriedades das versões abaixo.",
|
"RadarrSupportsCustomConditionsAgainstTheReleasePropertiesBelow": "O Radar suporta condições personalizadas em relação às propriedades das versões abaixo.",
|
||||||
"RadarrSupportsAnyRSSMovieListsAsWellAsTheOneStatedBelow": "O Radarr suporta qualquer lista de filmes em RSS, bem como as demais listadas abaixo.",
|
"RadarrSupportsAnyRSSMovieListsAsWellAsTheOneStatedBelow": "O Radarr suporta qualquer lista de filmes em RSS, bem como as demais listadas abaixo.",
|
||||||
"RadarrSupportsAnyDownloadClient": "O Radarr suporta muitos dos clientes de transferências torrent e usenet mais populares.",
|
"RadarrSupportsAnyDownloadClient": "O Radarr suporta muitos dos clientes de transferências torrent e usenet mais populares.",
|
||||||
"QualityProfileDeleteConfirm": "Tem a certeza que quer eliminar o perfil de qualidade {0}?",
|
"QualityProfileDeleteConfirm": "Tem a certeza que pretende eliminar o perfil de qualidade {0}",
|
||||||
"ProxyUsernameHelpText": "Apenas insira o utilizador e a palavra-passe caso seja requerido. Caso contrário, deixe em branco.",
|
"ProxyUsernameHelpText": "Apenas insira o utilizador e a palavra-passe caso seja requerido. Caso contrário, deixe em branco.",
|
||||||
"ProxyPasswordHelpText": "Apenas insira o utilizador e a palavra-passe caso seja requerido. Caso contrário, deixe em branco.",
|
"ProxyPasswordHelpText": "Apenas insira o utilizador e a palavra-passe caso seja requerido. Caso contrário, deixe em branco.",
|
||||||
"ProxyBypassFilterHelpText": "Utilizar \",\" como separador e \"*.\" como caráter universal para subdomínios",
|
"ProxyBypassFilterHelpText": "Utilizar \",\" como separador e \"*.\" como caráter universal para subdomínios",
|
||||||
"Proper": "Proper",
|
"Proper": "Correta",
|
||||||
"MovieInfoLanguage": "Idioma das informações do filme",
|
"MovieInfoLanguage": "Idioma das informações do filme",
|
||||||
"MaintenanceRelease": "Versão de manutenção: correção de bugs e outras melhorias. Veja o Github Commit History para obter mais detalhes",
|
"MaintenanceRelease": "Versão de manutenção: correção de bugs e outras melhorias. Veja o Github Commit History para obter mais detalhes",
|
||||||
"TorrentDelay": "Atraso para torrents",
|
"TorrentDelay": "Atraso para torrents",
|
||||||
@@ -898,7 +898,7 @@
|
|||||||
"PreferTorrent": "Preferir torrent",
|
"PreferTorrent": "Preferir torrent",
|
||||||
"PreferUsenet": "Preferir Usenet",
|
"PreferUsenet": "Preferir Usenet",
|
||||||
"QualitiesHelpText": "Qualidades mais acima na lista têm maior prioridade, as que estão no mesmo grupo têm prioridade igual. Apenas qualidades verificadas são desejadas",
|
"QualitiesHelpText": "Qualidades mais acima na lista têm maior prioridade, as que estão no mesmo grupo têm prioridade igual. Apenas qualidades verificadas são desejadas",
|
||||||
"QualityProfileInUse": "Não é possível eliminar um perfil de qualidade anexado a um filme",
|
"QualityProfileInUse": "Não é possível eliminar um perfil de qualidade quando associado a um filme, lista ou coleção",
|
||||||
"QueueIsEmpty": "A fila está vazia",
|
"QueueIsEmpty": "A fila está vazia",
|
||||||
"RadarrCalendarFeed": "Feed de calendário do Radarr",
|
"RadarrCalendarFeed": "Feed de calendário do Radarr",
|
||||||
"ReleasedMsg": "Filme lançado",
|
"ReleasedMsg": "Filme lançado",
|
||||||
@@ -996,7 +996,7 @@
|
|||||||
"SelectMovie": "Selecionar filme",
|
"SelectMovie": "Selecionar filme",
|
||||||
"SelectQuality": "Selecionar qualidade",
|
"SelectQuality": "Selecionar qualidade",
|
||||||
"Small": "Pequeno",
|
"Small": "Pequeno",
|
||||||
"Socks4": "Socks4",
|
"Socks4": "SOCKS4",
|
||||||
"Socks5": "Socks5 (suporte a TOR)",
|
"Socks5": "Socks5 (suporte a TOR)",
|
||||||
"SomeResultsHiddenFilter": "Alguns resultados estão ocultos pelo filtro aplicado",
|
"SomeResultsHiddenFilter": "Alguns resultados estão ocultos pelo filtro aplicado",
|
||||||
"Sunday": "Domingo",
|
"Sunday": "Domingo",
|
||||||
@@ -1050,7 +1050,7 @@
|
|||||||
"RemotePathMappingCheckLocalWrongOSPath": "O cliente local {0} coloca as transferências em {1}, mas esse não é um caminho {2} válido. Revise as definições do cliente de transferências.",
|
"RemotePathMappingCheckLocalWrongOSPath": "O cliente local {0} coloca as transferências em {1}, mas esse não é um caminho {2} válido. Revise as definições do cliente de transferências.",
|
||||||
"RemotePathMappingCheckLocalFolderMissing": "O cliente remoto {0} coloca as transferências em {1}, mas esse diretório parece não existir. O mapeamento de caminho remoto está provavelmente ausente ou incorreto.",
|
"RemotePathMappingCheckLocalFolderMissing": "O cliente remoto {0} coloca as transferências em {1}, mas esse diretório parece não existir. O mapeamento de caminho remoto está provavelmente ausente ou incorreto.",
|
||||||
"RemotePathMappingCheckImportFailed": "O Radarr não conseguiu importar um filme. Verifique os ficheiros de log para obter mais informações.",
|
"RemotePathMappingCheckImportFailed": "O Radarr não conseguiu importar um filme. Verifique os ficheiros de log para obter mais informações.",
|
||||||
"RemotePathMappingCheckGenericPermissions": "O cliente {0} coloca as transferências em {1}, mas o Radarr não pode ver esse diretório. Pode ser necessário ajustar as permissões da pasta.",
|
"RemotePathMappingCheckGenericPermissions": "O cliente de download {0} coloca as transferências em {1}, mas o Radarr não consegue ver essa pasta. Poderá ser necessário ajustar as permissões da pasta.",
|
||||||
"RemotePathMappingCheckFolderPermissions": "O Radarr pode ver, mas não pode acessar ao diretório de transferências {0}. Provável erro de permissões.",
|
"RemotePathMappingCheckFolderPermissions": "O Radarr pode ver, mas não pode acessar ao diretório de transferências {0}. Provável erro de permissões.",
|
||||||
"RemotePathMappingCheckFilesWrongOSPath": "O cliente de transferências remoto {0} relatou ficheiros em {1}, mas este não é um caminho {2} válido. Revise os mapeamentos de caminho remoto e as definições do cliente de transferências.",
|
"RemotePathMappingCheckFilesWrongOSPath": "O cliente de transferências remoto {0} relatou ficheiros em {1}, mas este não é um caminho {2} válido. Revise os mapeamentos de caminho remoto e as definições do cliente de transferências.",
|
||||||
"RemotePathMappingCheckFilesLocalWrongOSPath": "O cliente de transferências local {0} relatou ficheiros em {1}, mas este não é um caminho {2} válido. Revise as definições do cliente de transferências.",
|
"RemotePathMappingCheckFilesLocalWrongOSPath": "O cliente de transferências local {0} relatou ficheiros em {1}, mas este não é um caminho {2} válido. Revise as definições do cliente de transferências.",
|
||||||
@@ -1111,17 +1111,39 @@
|
|||||||
"SetReleaseGroup": "Definir Grupo de Lançamento",
|
"SetReleaseGroup": "Definir Grupo de Lançamento",
|
||||||
"Started": "Começado",
|
"Started": "Começado",
|
||||||
"Waiting": "À espera",
|
"Waiting": "À espera",
|
||||||
"Database": "base de dados",
|
"Database": "Base de dados",
|
||||||
"AllCollectionsHiddenDueToFilter": "Todos os filmes estão ocultos devido ao filtro aplicado.",
|
"AllCollectionsHiddenDueToFilter": "Todos os filmes estão ocultos devido ao filtro aplicado.",
|
||||||
"Collections": "Coleção",
|
"Collections": "Coleção",
|
||||||
"RssSyncHelpText": "Intervalo em minutos. Defina como zero para desativar (isso parará toda a captura automática)",
|
"RssSyncHelpText": "Intervalo em minutos. Defina como zero para desativar (isso parará toda a captura automática)",
|
||||||
"MonitorMovies": "Monitorar filme",
|
"MonitorMovies": "Monitorizar filmes",
|
||||||
"NoCollections": "Nenhum filme encontrado. Para começar, adiciona um novo filme ou importa alguns já existentes.",
|
"NoCollections": "Nenhuma coleção encontrada. Para iniciar, poderás adicionar um novo filme ou importar alguns já existentes",
|
||||||
"MovieAndCollection": "Filme e Coleção",
|
"MovieAndCollection": "Filme e Coleção",
|
||||||
"CollectionsSelectedInterp": "{0} Coleções Selecionadas",
|
"CollectionsSelectedInterp": "{0} Coleções Selecionadas",
|
||||||
"EditCollection": "Editar Coleção",
|
"EditCollection": "Editar Coleção",
|
||||||
"ChooseImportMode": "Selecionar Modo de Importação",
|
"ChooseImportMode": "Selecionar Modo de Importação",
|
||||||
"InstanceName": "Nome da Instancia",
|
"InstanceName": "Nome da Instancia",
|
||||||
"CollectionOptions": "Opções de Coleção",
|
"CollectionOptions": "Opções de Coleção",
|
||||||
"CollectionShowDetailsHelpText": "Mostrar estado da coleção e proprieades"
|
"CollectionShowDetailsHelpText": "Mostrar estado da coleção e proprieades",
|
||||||
|
"RottenTomatoesRating": "Avaliação Tomato",
|
||||||
|
"SearchOnAddCollectionHelpText": "Procurar por filmes nesta coleção quando adicionado a uma biblioteca",
|
||||||
|
"OnMovieAdded": "Quando adicionado filme",
|
||||||
|
"OnMovieAddedHelpText": "Acção a aplicar quando o filme for adicionado",
|
||||||
|
"RefreshCollections": "Refrescar coleções",
|
||||||
|
"RefreshMonitoredIntervalHelpText": "Frequência de atualização da monitorização de downloads dos clientes de download, mínimo 1 minuto",
|
||||||
|
"ScrollMovies": "Rolar filmes",
|
||||||
|
"ShowOverview": "Mostrar visão global",
|
||||||
|
"ShowPosters": "Mostrar posteres",
|
||||||
|
"UnableToLoadCollections": "Impossível carregar coleções",
|
||||||
|
"TotalMovies": "Total de filmes",
|
||||||
|
"MonitorCollection": "Monitorizar coleção",
|
||||||
|
"MonitoredCollectionHelpText": "Monitorizar para automaticamente adicionar filmes desta coleção à biblioteca",
|
||||||
|
"InstanceNameHelpText": "Nome da instância na aba e nome da aplicação para Syslog",
|
||||||
|
"ApplicationURL": "URL da aplicação",
|
||||||
|
"ApplicationUrlHelpText": "O URL desta aplicação externa, incluindo http(s)://, porta e URL base",
|
||||||
|
"CollectionShowOverviewsHelpText": "Mostrar visão global da coleção",
|
||||||
|
"CollectionShowPostersHelpText": "Mostrar os posteres da coleção",
|
||||||
|
"MovieCollectionMissingRoot": "Falta a pasta base para a coleção de filmes: {0}",
|
||||||
|
"MovieCollectionMultipleMissingRoots": "Faltam várias pastas base para as coleções de filmes: {0}",
|
||||||
|
"MovieOnly": "Apenas filme",
|
||||||
|
"ShowCollectionDetails": "Mostrar estado da coleção"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"IndexerStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a falhas",
|
"IndexerStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a falhas",
|
||||||
"IndexersSettingsSummary": "Indexadores e restrições de lançamento",
|
"IndexersSettingsSummary": "Indexadores e restrições de lançamento",
|
||||||
"IndexerSettings": "Configurações do indexador",
|
"IndexerSettings": "Configurações do indexador",
|
||||||
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa interativa ativada, o Radarr não fornecerá nenhum resultado de pesquisa interativo",
|
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa interativa ativada, o Radarr não fornecerá nenhum resultado de pesquisa interativa",
|
||||||
"IndexerSearchCheckNoAvailableIndexersMessage": "Todos os indexadores com capacidade de pesquisa estão temporariamente indisponíveis devido a erros recentes do indexador",
|
"IndexerSearchCheckNoAvailableIndexersMessage": "Todos os indexadores com capacidade de pesquisa estão temporariamente indisponíveis devido a erros recentes do indexador",
|
||||||
"IndexerSearchCheckNoAutomaticMessage": "Nenhum indexador disponível com a Pesquisa automática habilitada, o Radarr não fornecerá nenhum resultado de pesquisa automática",
|
"IndexerSearchCheckNoAutomaticMessage": "Nenhum indexador disponível com a Pesquisa automática habilitada, o Radarr não fornecerá nenhum resultado de pesquisa automática",
|
||||||
"Indexers": "Indexadores",
|
"Indexers": "Indexadores",
|
||||||
@@ -846,7 +846,7 @@
|
|||||||
"QualitySettingsSummary": "Tamanhos de qualidade e nomenclatura",
|
"QualitySettingsSummary": "Tamanhos de qualidade e nomenclatura",
|
||||||
"QualitySettings": "Configurações de qualidade",
|
"QualitySettings": "Configurações de qualidade",
|
||||||
"QualityProfiles": "Perfis de qualidade",
|
"QualityProfiles": "Perfis de qualidade",
|
||||||
"QualityProfileInUse": "Não é possível excluir um perfil de qualidade que está vinculado a um filme",
|
"QualityProfileInUse": "Não é possível excluir um perfil de qualidade anexado a um filme, lista ou coleção",
|
||||||
"QualityProfileDeleteConfirm": "Tem certeza que deseja excluir o perfil de qualidade {0}",
|
"QualityProfileDeleteConfirm": "Tem certeza que deseja excluir o perfil de qualidade {0}",
|
||||||
"QualityProfile": "Perfil de qualidade",
|
"QualityProfile": "Perfil de qualidade",
|
||||||
"QualityOrLangCutoffHasNotBeenMet": "Limite de qualidade ou de idioma não atingido",
|
"QualityOrLangCutoffHasNotBeenMet": "Limite de qualidade ou de idioma não atingido",
|
||||||
@@ -928,8 +928,8 @@
|
|||||||
"OnImport": "Ao importar",
|
"OnImport": "Ao importar",
|
||||||
"OnHealthIssueHelpText": "Ao ter problema de integridade",
|
"OnHealthIssueHelpText": "Ao ter problema de integridade",
|
||||||
"OnHealthIssue": "Em Problema de Saúde",
|
"OnHealthIssue": "Em Problema de Saúde",
|
||||||
"OnGrabHelpText": "Ao obter",
|
"OnGrabHelpText": "Em Espera",
|
||||||
"OnGrab": "Ao Baixar",
|
"OnGrab": "Em Espera",
|
||||||
"OnDownloadHelpText": "Ao importar",
|
"OnDownloadHelpText": "Ao importar",
|
||||||
"Ok": "Ok",
|
"Ok": "Ok",
|
||||||
"OAuthPopupMessage": "Os pop-ups estão bloqueados em seu navegador",
|
"OAuthPopupMessage": "Os pop-ups estão bloqueados em seu navegador",
|
||||||
@@ -1117,8 +1117,8 @@
|
|||||||
"InstanceName": "Nome da instância",
|
"InstanceName": "Nome da instância",
|
||||||
"InstanceNameHelpText": "Nome da instância na guia e para o nome do aplicativo Syslog",
|
"InstanceNameHelpText": "Nome da instância na guia e para o nome do aplicativo Syslog",
|
||||||
"AllCollectionsHiddenDueToFilter": "Todos os filmes estão ocultos devido ao filtro aplicado.",
|
"AllCollectionsHiddenDueToFilter": "Todos os filmes estão ocultos devido ao filtro aplicado.",
|
||||||
"Collections": "Coleção",
|
"Collections": "Coleções",
|
||||||
"MonitorMovies": "Monitorar filme",
|
"MonitorMovies": "Monitorar Filmes",
|
||||||
"NoCollections": "Nenhum filme encontrado. Para começar, adicione um novo filme ou importe alguns existentes",
|
"NoCollections": "Nenhum filme encontrado. Para começar, adicione um novo filme ou importe alguns existentes",
|
||||||
"MovieOnly": "Somente Filme",
|
"MovieOnly": "Somente Filme",
|
||||||
"UnableToLoadCollections": "Não foi possível carregar as coleções",
|
"UnableToLoadCollections": "Não foi possível carregar as coleções",
|
||||||
@@ -1142,5 +1142,8 @@
|
|||||||
"CollectionShowDetailsHelpText": "Mostrar estado e propriedades da coleção",
|
"CollectionShowDetailsHelpText": "Mostrar estado e propriedades da coleção",
|
||||||
"CollectionShowOverviewsHelpText": "Mostrar visão geral da coleção",
|
"CollectionShowOverviewsHelpText": "Mostrar visão geral da coleção",
|
||||||
"CollectionShowPostersHelpText": "Mostrar pôsteres de itens da coleção",
|
"CollectionShowPostersHelpText": "Mostrar pôsteres de itens da coleção",
|
||||||
"RottenTomatoesRating": "Avaliação Tomato"
|
"RottenTomatoesRating": "Avaliação Tomato",
|
||||||
|
"TotalMovies": "Total de Filmes",
|
||||||
|
"ApplicationURL": "URL da Aplicação",
|
||||||
|
"ApplicationUrlHelpText": "O URL externa deste aplicativo, incluindo http(s)://, porta e base de URL"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
"Activity": "Activitate",
|
"Activity": "Activitate",
|
||||||
"Actions": "Acțiuni",
|
"Actions": "Acțiuni",
|
||||||
"About": "Despre",
|
"About": "Despre",
|
||||||
"IndexerStatusCheckAllClientMessage": "Niciun indexator nu este disponibil datorită eșuărilor",
|
"IndexerStatusCheckAllClientMessage": "Niciun indexator nu este disponibil datorită erorilor",
|
||||||
"IndexersSettingsSummary": "Restricții pentru indexatori și apariții",
|
"IndexersSettingsSummary": "Restricții pentru indexatori și apariții",
|
||||||
"IndexerSearchCheckNoInteractiveMessage": "Niciun indexator cu Căutare interactivă nu este activ, Radarr nu va afișa niciun rezultat de căutare interactivă",
|
"IndexerSearchCheckNoInteractiveMessage": "Niciun indexator cu Căutare interactivă nu este activ, Radarr nu va afișa niciun rezultat de căutare interactivă",
|
||||||
"IndexerSearchCheckNoAvailableIndexersMessage": "Toți indexatorii ce suportă căutare sunt indisponibili temporar datorită erorilor",
|
"IndexerSearchCheckNoAvailableIndexersMessage": "Toți indexatorii ce suportă căutare sunt indisponibili temporar datorită erorilor",
|
||||||
@@ -62,11 +62,11 @@
|
|||||||
"Ignored": "Ignorat",
|
"Ignored": "Ignorat",
|
||||||
"iCalLink": "Legătură iCal",
|
"iCalLink": "Legătură iCal",
|
||||||
"Host": "Gazdă",
|
"Host": "Gazdă",
|
||||||
"History": "Istorie",
|
"History": "Istoric",
|
||||||
"HideAdvanced": "Ascunde Avansat",
|
"HideAdvanced": "Ascunde Avansat",
|
||||||
"Health": "Sănătate",
|
"Health": "Sănătate",
|
||||||
"GrabSelected": "Prinderea selectată",
|
"GrabSelected": "Prinderea selectată",
|
||||||
"Grabbed": "Prins",
|
"Grabbed": "În curs de descărcare",
|
||||||
"Genres": "Genuri",
|
"Genres": "Genuri",
|
||||||
"GeneralSettingsSummary": "Port, SSL, utilizator/parolă, proxy, statistici și actualizări",
|
"GeneralSettingsSummary": "Port, SSL, utilizator/parolă, proxy, statistici și actualizări",
|
||||||
"General": "General",
|
"General": "General",
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
"Folder": "Dosar",
|
"Folder": "Dosar",
|
||||||
"Filter": "Filtru",
|
"Filter": "Filtru",
|
||||||
"Files": "Fișiere",
|
"Files": "Fișiere",
|
||||||
"Filename": "Numele Fișierului",
|
"Filename": "Numele fișierului",
|
||||||
"FileManagement": "Administrarea de fișiere",
|
"FileManagement": "Administrarea de fișiere",
|
||||||
"FailedDownloadHandling": "Procesarea descărcării a eșuat",
|
"FailedDownloadHandling": "Procesarea descărcării a eșuat",
|
||||||
"Failed": "Eșuat",
|
"Failed": "Eșuat",
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
"Events": "Evenimente",
|
"Events": "Evenimente",
|
||||||
"Edit": "Editează",
|
"Edit": "Editează",
|
||||||
"Downloaded": "Descărcat",
|
"Downloaded": "Descărcat",
|
||||||
"DownloadClientStatusCheckSingleClientMessage": "Clienții de descărcare sunt indisponibili datorită erorii: {0}",
|
"DownloadClientStatusCheckSingleClientMessage": "Clienții de descărcare sunt indisponibili datorită erorilor: {0}",
|
||||||
"DownloadClientStatusCheckAllClientMessage": "Toți clienții de descărcare sunt indisponibili datorită erorilor",
|
"DownloadClientStatusCheckAllClientMessage": "Toți clienții de descărcare sunt indisponibili datorită erorilor",
|
||||||
"ProfilesSettingsSummary": "Calitate, Limbă și Profile de Întârziere",
|
"ProfilesSettingsSummary": "Calitate, Limbă și Profile de Întârziere",
|
||||||
"Profiles": "Profile",
|
"Profiles": "Profile",
|
||||||
@@ -143,9 +143,9 @@
|
|||||||
"Language": "Limbă",
|
"Language": "Limbă",
|
||||||
"KeyboardShortcuts": "Scurtături din tastatură",
|
"KeyboardShortcuts": "Scurtături din tastatură",
|
||||||
"Info": "Info",
|
"Info": "Info",
|
||||||
"IndexerStatusCheckSingleClientMessage": "Indexator indisponibil datorită erorilor: {0}",
|
"IndexerStatusCheckSingleClientMessage": "Indexatoare indisponibile datorită erorilor: {0}",
|
||||||
"ImportExistingMovies": "Importă Filme Existente",
|
"ImportExistingMovies": "Importă Filme Existente",
|
||||||
"HealthNoIssues": "Nicio problemă în configurare",
|
"HealthNoIssues": "Nicio problemă de configurare",
|
||||||
"HardlinkCopyFiles": "Hardlink/Copiază Fișiere",
|
"HardlinkCopyFiles": "Hardlink/Copiază Fișiere",
|
||||||
"Extension": "Extensie",
|
"Extension": "Extensie",
|
||||||
"Error": "Eroare",
|
"Error": "Eroare",
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
"QualityProfile": "Profil de Calitate",
|
"QualityProfile": "Profil de Calitate",
|
||||||
"QualityDefinitions": "Definiții de calitate",
|
"QualityDefinitions": "Definiții de calitate",
|
||||||
"Quality": "Calitate",
|
"Quality": "Calitate",
|
||||||
"PtpOldSettingsCheckMessage": "Următorul indexer PassThePopcorn are setări depreciate și ar trebui actualizate: {0}",
|
"PtpOldSettingsCheckMessage": "Următoarele indexatoare PassThePopcorn au setări depreciate și ar trebui actualizate: {0}",
|
||||||
"ProxyCheckResolveIpMessage": "Nu am putut găsi adresa IP pentru Hostul Proxy Configurat {0}",
|
"ProxyCheckResolveIpMessage": "Nu am putut găsi adresa IP pentru Hostul Proxy Configurat {0}",
|
||||||
"ProxyCheckFailedToTestMessage": "Nu am putut testa proxy: {0}",
|
"ProxyCheckFailedToTestMessage": "Nu am putut testa proxy: {0}",
|
||||||
"ProxyCheckBadRequestMessage": "Testul proxy a eșuat. StatusCode: {0}",
|
"ProxyCheckBadRequestMessage": "Testul proxy a eșuat. StatusCode: {0}",
|
||||||
@@ -260,7 +260,7 @@
|
|||||||
"Queue": "Coadă",
|
"Queue": "Coadă",
|
||||||
"QualitySettingsSummary": "Calitate - mărimi și denumiri",
|
"QualitySettingsSummary": "Calitate - mărimi și denumiri",
|
||||||
"AutoRedownloadFailedHelpText": "Căutați automat și încercați să descărcați o versiune diferită",
|
"AutoRedownloadFailedHelpText": "Căutați automat și încercați să descărcați o versiune diferită",
|
||||||
"BackupIntervalHelpText": "Interval între copiile de rezervă automate",
|
"BackupIntervalHelpText": "Interval între crearea copiile de siguranță automate",
|
||||||
"ApplyTagsHelpTexts1": "Cum se aplică etichete filmelor selectate",
|
"ApplyTagsHelpTexts1": "Cum se aplică etichete filmelor selectate",
|
||||||
"CustomFormatUnknownCondition": "Stare necunoscută pentru formatul personalizat „{0}”",
|
"CustomFormatUnknownCondition": "Stare necunoscută pentru formatul personalizat „{0}”",
|
||||||
"QualitySettings": "Setări de calitate",
|
"QualitySettings": "Setări de calitate",
|
||||||
@@ -337,7 +337,7 @@
|
|||||||
"ThisCannotBeCancelled": "Acest lucru nu poate fi anulat odată pornit fără a reporni Radarr.",
|
"ThisCannotBeCancelled": "Acest lucru nu poate fi anulat odată pornit fără a reporni Radarr.",
|
||||||
"TorrentDelayHelpText": "Întârziați în câteva minute pentru a aștepta înainte de a apuca un torent",
|
"TorrentDelayHelpText": "Întârziați în câteva minute pentru a aștepta înainte de a apuca un torent",
|
||||||
"Trigger": "Declanșator",
|
"Trigger": "Declanșator",
|
||||||
"UnableToAddANewIndexerPleaseTryAgain": "Imposibil de adăugat un nou indexer, încercați din nou.",
|
"UnableToAddANewIndexerPleaseTryAgain": "Nu se poate adăuga un nou indexator, vă rugăm să încercați din nou.",
|
||||||
"UnableToAddANewNotificationPleaseTryAgain": "Imposibil de adăugat o nouă notificare, încercați din nou.",
|
"UnableToAddANewNotificationPleaseTryAgain": "Imposibil de adăugat o nouă notificare, încercați din nou.",
|
||||||
"UnableToAddANewRemotePathMappingPleaseTryAgain": "Imposibil de adăugat o nouă mapare a căilor la distanță, încercați din nou.",
|
"UnableToAddANewRemotePathMappingPleaseTryAgain": "Imposibil de adăugat o nouă mapare a căilor la distanță, încercați din nou.",
|
||||||
"UnableToImportCheckLogs": "Descărcat - Imposibil de importat: verificați jurnalele pentru detalii",
|
"UnableToImportCheckLogs": "Descărcat - Imposibil de importat: verificați jurnalele pentru detalii",
|
||||||
@@ -369,7 +369,7 @@
|
|||||||
"Importing": "Importând",
|
"Importing": "Importând",
|
||||||
"AcceptConfirmationModal": "Acceptați Modul de confirmare",
|
"AcceptConfirmationModal": "Acceptați Modul de confirmare",
|
||||||
"LastExecution": "Ultima executare",
|
"LastExecution": "Ultima executare",
|
||||||
"DBMigration": "Migrarea DB",
|
"DBMigration": "Migrarea BD",
|
||||||
"Manual": "Manual",
|
"Manual": "Manual",
|
||||||
"OnHealthIssue": "Cu privire la problema sănătății",
|
"OnHealthIssue": "Cu privire la problema sănătății",
|
||||||
"OnImport": "La import",
|
"OnImport": "La import",
|
||||||
@@ -388,8 +388,8 @@
|
|||||||
"AvailabilityDelayHelpText": "Suma de timp înainte sau după data disponibilă pentru căutarea filmului",
|
"AvailabilityDelayHelpText": "Suma de timp înainte sau după data disponibilă pentru căutarea filmului",
|
||||||
"Announced": "Anunțat",
|
"Announced": "Anunțat",
|
||||||
"AddRemotePathMapping": "Adăugați maparea căilor la distanță",
|
"AddRemotePathMapping": "Adăugați maparea căilor la distanță",
|
||||||
"BackupRetentionHelpText": "Copiile de rezervă automate mai vechi de perioada de păstrare vor fi curățate automat",
|
"BackupRetentionHelpText": "Copiile de siguranță automate mai vechi decât perioada de păstrare vor fi curățate automat",
|
||||||
"Backups": "Copii de rezervă",
|
"Backups": "Copii de siguranță",
|
||||||
"BeforeUpdate": "Înainte de actualizare",
|
"BeforeUpdate": "Înainte de actualizare",
|
||||||
"BindAddress": "Adresa de legare",
|
"BindAddress": "Adresa de legare",
|
||||||
"CancelProcessing": "Anulați procesarea",
|
"CancelProcessing": "Anulați procesarea",
|
||||||
@@ -404,7 +404,7 @@
|
|||||||
"DeleteMovieFolderLabel": "Ștergeți dosarul filmului",
|
"DeleteMovieFolderLabel": "Ștergeți dosarul filmului",
|
||||||
"DockerUpdater": "actualizați containerul de andocare pentru a primi actualizarea",
|
"DockerUpdater": "actualizați containerul de andocare pentru a primi actualizarea",
|
||||||
"EditGroups": "Editați grupurile",
|
"EditGroups": "Editați grupurile",
|
||||||
"EditIndexer": "Editați Indexer",
|
"EditIndexer": "Editați indexator",
|
||||||
"Enabled": "Activat",
|
"Enabled": "Activat",
|
||||||
"ExcludeMovie": "Excludeți filmul",
|
"ExcludeMovie": "Excludeți filmul",
|
||||||
"ExcludeTitle": "Excludeți {0}? Acest lucru îl va împiedica pe Radarr să adauge automat prin sincronizarea listei.",
|
"ExcludeTitle": "Excludeți {0}? Acest lucru îl va împiedica pe Radarr să adauge automat prin sincronizarea listei.",
|
||||||
@@ -413,7 +413,7 @@
|
|||||||
"HttpHttps": "HTTP (S)",
|
"HttpHttps": "HTTP (S)",
|
||||||
"ICalFeed": "Feed iCal",
|
"ICalFeed": "Feed iCal",
|
||||||
"ICalHttpUrlHelpText": "Copiați această adresă URL către clienții dvs. sau faceți clic pentru a vă abona dacă browserul dvs. acceptă webcal",
|
"ICalHttpUrlHelpText": "Copiați această adresă URL către clienții dvs. sau faceți clic pentru a vă abona dacă browserul dvs. acceptă webcal",
|
||||||
"Add": "Adăuga",
|
"Add": "Adaugă",
|
||||||
"AddCustomFormat": "Adăugați format personalizat",
|
"AddCustomFormat": "Adăugați format personalizat",
|
||||||
"AddDelayProfile": "Adăugați un profil de întârziere",
|
"AddDelayProfile": "Adăugați un profil de întârziere",
|
||||||
"AddDownloadClient": "Adăugați client de descărcare",
|
"AddDownloadClient": "Adăugați client de descărcare",
|
||||||
@@ -440,7 +440,7 @@
|
|||||||
"ChmodFolderHelpTextWarning": "Acest lucru funcționează numai dacă utilizatorul care rulează Radarr este proprietarul fișierului. Este mai bine să vă asigurați că clientul de descărcare setează corect permisiunile.",
|
"ChmodFolderHelpTextWarning": "Acest lucru funcționează numai dacă utilizatorul care rulează Radarr este proprietarul fișierului. Este mai bine să vă asigurați că clientul de descărcare setează corect permisiunile.",
|
||||||
"ChmodGroup": "chmod Group",
|
"ChmodGroup": "chmod Group",
|
||||||
"ChmodGroupHelpText": "Numele grupului sau gid. Utilizați gid pentru sistemele de fișiere la distanță.",
|
"ChmodGroupHelpText": "Numele grupului sau gid. Utilizați gid pentru sistemele de fișiere la distanță.",
|
||||||
"DeleteBackup": "Ștergeți Backup",
|
"DeleteBackup": "Ștergeți copie de siguranță",
|
||||||
"DeleteCustomFormat": "Ștergeți formatul personalizat",
|
"DeleteCustomFormat": "Ștergeți formatul personalizat",
|
||||||
"DeletedMsg": "Filmul a fost șters din TMDb",
|
"DeletedMsg": "Filmul a fost șters din TMDb",
|
||||||
"DeleteDownloadClient": "Ștergeți clientul de descărcare",
|
"DeleteDownloadClient": "Ștergeți clientul de descărcare",
|
||||||
@@ -448,12 +448,12 @@
|
|||||||
"Connection": "Conexiuni",
|
"Connection": "Conexiuni",
|
||||||
"CantFindMovie": "De ce nu-mi găsesc filmul?",
|
"CantFindMovie": "De ce nu-mi găsesc filmul?",
|
||||||
"CertificateValidationHelpText": "Modificați cât de strictă este validarea certificării HTTPS",
|
"CertificateValidationHelpText": "Modificați cât de strictă este validarea certificării HTTPS",
|
||||||
"AddIndexer": "Adăugați Indexer",
|
"AddIndexer": "Adăugați Indexator",
|
||||||
"AutomaticSearch": "Căutare automată",
|
"AutomaticSearch": "Căutare automată",
|
||||||
"ClientPriority": "Prioritatea clientului",
|
"ClientPriority": "Prioritatea clientului",
|
||||||
"CertificationCountry": "Țara certificării",
|
"CertificationCountry": "Țara certificării",
|
||||||
"CertificationCountryHelpText": "Selectați Țara pentru certificări de film",
|
"CertificationCountryHelpText": "Selectați Țara pentru certificări de film",
|
||||||
"HomePage": "Pagina principala",
|
"HomePage": "Pagina principală",
|
||||||
"IncludeHealthWarningsHelpText": "Includeți avertismente de sănătate",
|
"IncludeHealthWarningsHelpText": "Includeți avertismente de sănătate",
|
||||||
"InvalidFormat": "Format invalid",
|
"InvalidFormat": "Format invalid",
|
||||||
"LastDuration": "lastDuration",
|
"LastDuration": "lastDuration",
|
||||||
@@ -508,7 +508,7 @@
|
|||||||
"ShowGenres": "Afișați genurile",
|
"ShowGenres": "Afișați genurile",
|
||||||
"SuggestTranslationChange": "Sugerează modificarea traducerii",
|
"SuggestTranslationChange": "Sugerează modificarea traducerii",
|
||||||
"TestAllClients": "Testați toți clienții",
|
"TestAllClients": "Testați toți clienții",
|
||||||
"TestAllIndexers": "Testați toți indexatorii",
|
"TestAllIndexers": "Testați toate indexatoarele",
|
||||||
"TestAllLists": "Testați toate listele",
|
"TestAllLists": "Testați toate listele",
|
||||||
"Queued": "În așteptare",
|
"Queued": "În așteptare",
|
||||||
"TMDb": "TMDb",
|
"TMDb": "TMDb",
|
||||||
@@ -524,8 +524,8 @@
|
|||||||
"DetailedProgressBarHelpText": "Afișați textul pe bara de progres",
|
"DetailedProgressBarHelpText": "Afișați textul pe bara de progres",
|
||||||
"EnableSSL": "Activați SSL",
|
"EnableSSL": "Activați SSL",
|
||||||
"IncludeCustomFormatWhenRenaming": "Includeți format personalizat la redenumire",
|
"IncludeCustomFormatWhenRenaming": "Includeți format personalizat la redenumire",
|
||||||
"IndexerLongTermStatusCheckAllClientMessage": "Toți indexatorii nu sunt disponibili din cauza unor eșecuri de mai mult de 6 ore",
|
"IndexerLongTermStatusCheckAllClientMessage": "Toți indexatorii sunt indisponibili datorită erorilor de mai mult de 6 ore",
|
||||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indexatori indisponibili din cauza unor eșecuri de mai mult de 6 ore: {0}",
|
"IndexerLongTermStatusCheckSingleClientMessage": "Indexatori indisponibili datorită erorilor de mai mult de 6 ore: {0}",
|
||||||
"LoadingMovieCreditsFailed": "Încărcarea creditelor filmului nu a reușit",
|
"LoadingMovieCreditsFailed": "Încărcarea creditelor filmului nu a reușit",
|
||||||
"LoadingMovieExtraFilesFailed": "Încărcarea fișierelor suplimentare ale filmului nu a reușit",
|
"LoadingMovieExtraFilesFailed": "Încărcarea fișierelor suplimentare ale filmului nu a reușit",
|
||||||
"LoadingMovieFilesFailed": "Încărcarea fișierelor film a eșuat",
|
"LoadingMovieFilesFailed": "Încărcarea fișierelor film a eșuat",
|
||||||
@@ -559,7 +559,7 @@
|
|||||||
"CleanLibraryLevel": "Nivel de bibliotecă curat",
|
"CleanLibraryLevel": "Nivel de bibliotecă curat",
|
||||||
"BranchUpdate": "Sucursală de utilizat pentru actualizarea Radarr",
|
"BranchUpdate": "Sucursală de utilizat pentru actualizarea Radarr",
|
||||||
"BranchUpdateMechanism": "Ramură utilizată de mecanismul extern de actualizare",
|
"BranchUpdateMechanism": "Ramură utilizată de mecanismul extern de actualizare",
|
||||||
"BypassProxyForLocalAddresses": "Bypass Proxy pentru adrese locale",
|
"BypassProxyForLocalAddresses": "Nu folosiți Proxy pentru adrese locale",
|
||||||
"MovieTitleHelpText": "Titlul filmului de exclus (poate avea orice sens)",
|
"MovieTitleHelpText": "Titlul filmului de exclus (poate avea orice sens)",
|
||||||
"Local": "Local",
|
"Local": "Local",
|
||||||
"MovieYear": "Anul filmului",
|
"MovieYear": "Anul filmului",
|
||||||
@@ -641,7 +641,7 @@
|
|||||||
"ClickToChangeQuality": "Faceți clic pentru a schimba calitatea",
|
"ClickToChangeQuality": "Faceți clic pentru a schimba calitatea",
|
||||||
"CloneFormatTag": "Etichetă de format clonare",
|
"CloneFormatTag": "Etichetă de format clonare",
|
||||||
"CloneIndexer": "Clonă Indexer",
|
"CloneIndexer": "Clonă Indexer",
|
||||||
"CloneProfile": "Profil de clonare",
|
"CloneProfile": "Clonați profil",
|
||||||
"CloseCurrentModal": "Închideți modul curent",
|
"CloseCurrentModal": "Închideți modul curent",
|
||||||
"ColonReplacement": "Înlocuirea colonului",
|
"ColonReplacement": "Înlocuirea colonului",
|
||||||
"ColonReplacementFormatHelpText": "Schimbați modul în care Radarr gestionează înlocuirea colonului",
|
"ColonReplacementFormatHelpText": "Schimbați modul în care Radarr gestionează înlocuirea colonului",
|
||||||
@@ -666,7 +666,7 @@
|
|||||||
"DefaultDelayProfile": "Acesta este profilul implicit. Se aplică tuturor filmelor care nu au un profil explicit.",
|
"DefaultDelayProfile": "Acesta este profilul implicit. Se aplică tuturor filmelor care nu au un profil explicit.",
|
||||||
"DelayingDownloadUntilInterp": "Întârzierea descărcării până la {0} la {1}",
|
"DelayingDownloadUntilInterp": "Întârzierea descărcării până la {0} la {1}",
|
||||||
"DelayProfile": "Profile de întârziere",
|
"DelayProfile": "Profile de întârziere",
|
||||||
"DeleteBackupMessageText": "Sigur doriți să ștergeți copia de rezervă „{0}”?",
|
"DeleteBackupMessageText": "Sigur doriți să ștergeți copia de siguranță „{0}”?",
|
||||||
"DeleteDelayProfile": "Ștergeți profilul de întârziere",
|
"DeleteDelayProfile": "Ștergeți profilul de întârziere",
|
||||||
"DeleteDownloadClientMessageText": "Sigur doriți să ștergeți clientul de descărcare „{0}”?",
|
"DeleteDownloadClientMessageText": "Sigur doriți să ștergeți clientul de descărcare „{0}”?",
|
||||||
"DeleteEmptyFolders": "Ștergeți folderele goale",
|
"DeleteEmptyFolders": "Ștergeți folderele goale",
|
||||||
@@ -686,13 +686,13 @@
|
|||||||
"DestinationPath": "Calea de destinație",
|
"DestinationPath": "Calea de destinație",
|
||||||
"DestinationRelativePath": "Calea relativă a destinației",
|
"DestinationRelativePath": "Calea relativă a destinației",
|
||||||
"DetailedProgressBar": "Bara de progres detaliată",
|
"DetailedProgressBar": "Bara de progres detaliată",
|
||||||
"Discord": "Discordie",
|
"Discord": "Discord",
|
||||||
"Docker": "Docher",
|
"Docker": "Docker",
|
||||||
"Donations": "Donații",
|
"Donations": "Donații",
|
||||||
"DoneEditingGroups": "Efectuat editarea grupurilor",
|
"DoneEditingGroups": "Efectuat editarea grupurilor",
|
||||||
"DoNotPrefer": "Nu preferați",
|
"DoNotPrefer": "Nu preferați",
|
||||||
"DoNotUpgradeAutomatically": "Nu faceți upgrade automat",
|
"DoNotUpgradeAutomatically": "Nu faceți upgrade automat",
|
||||||
"DownloadClientSettings": "Descărcați setările clientului",
|
"DownloadClientSettings": "Setări client de descărcare",
|
||||||
"DownloadClientUnavailable": "Clientul de descărcare nu este disponibil",
|
"DownloadClientUnavailable": "Clientul de descărcare nu este disponibil",
|
||||||
"DownloadedAndMonitored": "Descărcat (monitorizat)",
|
"DownloadedAndMonitored": "Descărcat (monitorizat)",
|
||||||
"DownloadedButNotMonitored": "Descărcat (fără monitorizare)",
|
"DownloadedButNotMonitored": "Descărcat (fără monitorizare)",
|
||||||
@@ -708,10 +708,10 @@
|
|||||||
"EditPerson": "Editați persoana",
|
"EditPerson": "Editați persoana",
|
||||||
"EditQualityProfile": "Editați profilul de calitate",
|
"EditQualityProfile": "Editați profilul de calitate",
|
||||||
"EditRestriction": "Editați restricția",
|
"EditRestriction": "Editați restricția",
|
||||||
"Enable": "Permite",
|
"Enable": "Activați",
|
||||||
"EnableAutoHelpText": "Dacă este activată, Filme vor fi adăugate automat la Radarr din această listă",
|
"EnableAutoHelpText": "Dacă este activată, Filme vor fi adăugate automat la Radarr din această listă",
|
||||||
"EnableAutomaticAdd": "Activați adăugarea automată",
|
"EnableAutomaticAdd": "Activați adăugarea automată",
|
||||||
"EnableAutomaticSearch": "Activați Căutarea automată",
|
"EnableAutomaticSearch": "Activați căutarea automată",
|
||||||
"EnableAutomaticSearchHelpText": "Va fi utilizat atunci când căutările automate sunt efectuate prin interfața de utilizare sau de către Radarr",
|
"EnableAutomaticSearchHelpText": "Va fi utilizat atunci când căutările automate sunt efectuate prin interfața de utilizare sau de către Radarr",
|
||||||
"EnableColorImpairedMode": "Activați modul afectat de culoare",
|
"EnableColorImpairedMode": "Activați modul afectat de culoare",
|
||||||
"EnableColorImpairedModeHelpText": "Stil modificat pentru a permite utilizatorilor cu deficiențe de culoare să distingă mai bine informațiile codificate prin culoare",
|
"EnableColorImpairedModeHelpText": "Stil modificat pentru a permite utilizatorilor cu deficiențe de culoare să distingă mai bine informațiile codificate prin culoare",
|
||||||
@@ -722,7 +722,7 @@
|
|||||||
"EnableMediaInfoHelpText": "Extrageți informații video, cum ar fi rezoluția, runtime și informații despre codec din fișiere. Acest lucru necesită ca Radarr să citească părți ale fișierului care pot provoca activitate ridicată pe disc sau rețea în timpul scanărilor.",
|
"EnableMediaInfoHelpText": "Extrageți informații video, cum ar fi rezoluția, runtime și informații despre codec din fișiere. Acest lucru necesită ca Radarr să citească părți ale fișierului care pot provoca activitate ridicată pe disc sau rețea în timpul scanărilor.",
|
||||||
"EnableRSS": "Activați RSS",
|
"EnableRSS": "Activați RSS",
|
||||||
"EnableSslHelpText": " Necesită repornirea în funcție de administrator pentru a intra în vigoare",
|
"EnableSslHelpText": " Necesită repornirea în funcție de administrator pentru a intra în vigoare",
|
||||||
"Ended": "Încheiat",
|
"Ended": "Finalizat",
|
||||||
"ErrorLoadingContents": "Eroare la încărcarea conținutului",
|
"ErrorLoadingContents": "Eroare la încărcarea conținutului",
|
||||||
"ErrorLoadingPreviews": "Eroare la încărcarea previzualizărilor",
|
"ErrorLoadingPreviews": "Eroare la încărcarea previzualizărilor",
|
||||||
"ErrorRestoringBackup": "Eroare la restaurarea copiei de rezervă",
|
"ErrorRestoringBackup": "Eroare la restaurarea copiei de rezervă",
|
||||||
@@ -751,7 +751,7 @@
|
|||||||
"FollowPerson": "Urmărește persoana",
|
"FollowPerson": "Urmărește persoana",
|
||||||
"ForMoreInformationOnTheIndividualDownloadClients": "Pentru mai multe informații despre clienții individuali de descărcare, faceți clic pe butoanele de informații.",
|
"ForMoreInformationOnTheIndividualDownloadClients": "Pentru mai multe informații despre clienții individuali de descărcare, faceți clic pe butoanele de informații.",
|
||||||
"ForMoreInformationOnTheIndividualIndexers": "Pentru mai multe informații despre indexatorii individuali, faceți clic pe butoanele de informații.",
|
"ForMoreInformationOnTheIndividualIndexers": "Pentru mai multe informații despre indexatorii individuali, faceți clic pe butoanele de informații.",
|
||||||
"GeneralSettings": "setari generale",
|
"GeneralSettings": "Setări generale",
|
||||||
"Global": "Global",
|
"Global": "Global",
|
||||||
"GoToInterp": "Accesați {0}",
|
"GoToInterp": "Accesați {0}",
|
||||||
"Grab": "Apuca",
|
"Grab": "Apuca",
|
||||||
@@ -776,7 +776,7 @@
|
|||||||
"IncludeRecommendationsHelpText": "Includeți filmele recomandate de Radarr în vizualizarea descoperire",
|
"IncludeRecommendationsHelpText": "Includeți filmele recomandate de Radarr în vizualizarea descoperire",
|
||||||
"IncludeUnmonitored": "Includeți Unmonitored",
|
"IncludeUnmonitored": "Includeți Unmonitored",
|
||||||
"ImportMovies": "Importați filme",
|
"ImportMovies": "Importați filme",
|
||||||
"IndexerPriority": "Prioritatea indexerului",
|
"IndexerPriority": "Prioritatea indexatorului",
|
||||||
"IndexerPriorityHelpText": "Prioritatea indexerului de la 1 (cea mai mare) la 50 (cea mai mică). Implicit: 25.",
|
"IndexerPriorityHelpText": "Prioritatea indexerului de la 1 (cea mai mare) la 50 (cea mai mică). Implicit: 25.",
|
||||||
"IndexerSettings": "Setări Indexer",
|
"IndexerSettings": "Setări Indexer",
|
||||||
"InstallLatest": "Instalați cele mai recente",
|
"InstallLatest": "Instalați cele mai recente",
|
||||||
@@ -875,7 +875,7 @@
|
|||||||
"Retention": "Retenţie",
|
"Retention": "Retenţie",
|
||||||
"RetentionHelpText": "Numai Usenet: Setați la zero pentru a seta pentru păstrarea nelimitată",
|
"RetentionHelpText": "Numai Usenet: Setați la zero pentru a seta pentru păstrarea nelimitată",
|
||||||
"RSS": "RSS",
|
"RSS": "RSS",
|
||||||
"RSSIsNotSupportedWithThisIndexer": "RSS nu este acceptat cu acest indexer",
|
"RSSIsNotSupportedWithThisIndexer": "RSS nu este suportat de acest indexator",
|
||||||
"RSSSyncInterval": "Interval de sincronizare RSS",
|
"RSSSyncInterval": "Interval de sincronizare RSS",
|
||||||
"RSSSyncIntervalHelpTextWarning": "Acest lucru se va aplica tuturor indexatorilor, vă rugăm să urmați regulile stabilite de aceștia",
|
"RSSSyncIntervalHelpTextWarning": "Acest lucru se va aplica tuturor indexatorilor, vă rugăm să urmați regulile stabilite de aceștia",
|
||||||
"SaveSettings": "Salvează setările",
|
"SaveSettings": "Salvează setările",
|
||||||
@@ -974,7 +974,7 @@
|
|||||||
"UnableToLoadGeneralSettings": "Nu se pot încărca setările generale",
|
"UnableToLoadGeneralSettings": "Nu se pot încărca setările generale",
|
||||||
"UnableToLoadHistory": "Istoricul nu poate fi încărcat",
|
"UnableToLoadHistory": "Istoricul nu poate fi încărcat",
|
||||||
"UnableToLoadIndexerOptions": "Nu se pot încărca opțiunile indexerului",
|
"UnableToLoadIndexerOptions": "Nu se pot încărca opțiunile indexerului",
|
||||||
"UnableToLoadIndexers": "Imposibil de încărcat indexatori",
|
"UnableToLoadIndexers": "Nu se pot încărca indexatoarele",
|
||||||
"UnableToLoadLanguages": "Nu se pot încărca limbile",
|
"UnableToLoadLanguages": "Nu se pot încărca limbile",
|
||||||
"UnableToLoadListExclusions": "Imposibil de încărcat excluderile din listă",
|
"UnableToLoadListExclusions": "Imposibil de încărcat excluderile din listă",
|
||||||
"UnableToLoadManualImportItems": "Imposibil de încărcat articole de import manual",
|
"UnableToLoadManualImportItems": "Imposibil de încărcat articole de import manual",
|
||||||
@@ -1052,7 +1052,7 @@
|
|||||||
"Blocklisted": "Listă Neagră",
|
"Blocklisted": "Listă Neagră",
|
||||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Sigur doriți să eliminați {0} elementul {1} din coadă?",
|
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Sigur doriți să eliminați {0} elementul {1} din coadă?",
|
||||||
"BlocklistReleases": "Lansare pe lista neagră",
|
"BlocklistReleases": "Lansare pe lista neagră",
|
||||||
"Filters": "Filtru",
|
"Filters": "Filtre",
|
||||||
"List": "Liste",
|
"List": "Liste",
|
||||||
"LocalPath": "Calea locală",
|
"LocalPath": "Calea locală",
|
||||||
"RemotePath": "Calea la distanță",
|
"RemotePath": "Calea la distanță",
|
||||||
|
|||||||
@@ -170,7 +170,7 @@
|
|||||||
"ExcludeTitle": "Исключить {0}? Radarr не будет автоматически добавлять сканируя лист.",
|
"ExcludeTitle": "Исключить {0}? Radarr не будет автоматически добавлять сканируя лист.",
|
||||||
"InCinemasMsg": "Фильмы в кинотеатрах",
|
"InCinemasMsg": "Фильмы в кинотеатрах",
|
||||||
"IncludeRecommendationsHelpText": "Включить в отображении найденного фильмы рекомендованные Radar",
|
"IncludeRecommendationsHelpText": "Включить в отображении найденного фильмы рекомендованные Radar",
|
||||||
"IndexerPriorityHelpText": "Приоритет индексаторов от 1 (наивысший) до 50 (низший). По-умолчанию: 25.",
|
"IndexerPriorityHelpText": "Приоритет индексатора от 1 (самый высокий) до 50 (самый низкий). По умолчанию: 25. Используется при захвате выпусков в качестве средства разрешения конфликтов для равных в остальном выпусков, Radarr по-прежнему будет использовать все включенные индексаторы для синхронизации и поиска RSS.",
|
||||||
"IndexerSearchCheckNoAvailableIndexersMessage": "Все индексаторы с возможностью поиска временно выключены из-за ошибок",
|
"IndexerSearchCheckNoAvailableIndexersMessage": "Все индексаторы с возможностью поиска временно выключены из-за ошибок",
|
||||||
"KeyboardShortcuts": "Горячие клавиши",
|
"KeyboardShortcuts": "Горячие клавиши",
|
||||||
"CantFindMovie": "Почему я не могу найти фильм?",
|
"CantFindMovie": "Почему я не могу найти фильм?",
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
"ApplyTagsHelpTexts2": "Добавить: добавить ярлыки к существующему списку",
|
"ApplyTagsHelpTexts2": "Добавить: добавить ярлыки к существующему списку",
|
||||||
"AptUpdater": "Используйте apt для установки обновления",
|
"AptUpdater": "Используйте apt для установки обновления",
|
||||||
"AuthForm": "Формы (Страница авторизации)",
|
"AuthForm": "Формы (Страница авторизации)",
|
||||||
"AreYouSureYouWantToRemoveSelectedItemsFromQueue": "Вы уверены, что хотите удалить {0} элемент{1} из очереди?",
|
"AreYouSureYouWantToRemoveSelectedItemsFromQueue": "Вы уверены, что хотите удалить {0} элемент из очереди?",
|
||||||
"BeforeUpdate": "До обновления",
|
"BeforeUpdate": "До обновления",
|
||||||
"BuiltIn": "Встроено",
|
"BuiltIn": "Встроено",
|
||||||
"CalendarOptions": "Настройки календаря",
|
"CalendarOptions": "Настройки календаря",
|
||||||
@@ -742,12 +742,12 @@
|
|||||||
"ChmodFolderHelpText": "Восьмеричный, применяется при импорте / переименовании к медиа-папкам и файлам (без битов выполнения)",
|
"ChmodFolderHelpText": "Восьмеричный, применяется при импорте / переименовании к медиа-папкам и файлам (без битов выполнения)",
|
||||||
"CheckDownloadClientForDetails": "проверьте клиент загрузки для более подробной информации",
|
"CheckDownloadClientForDetails": "проверьте клиент загрузки для более подробной информации",
|
||||||
"CertValidationNoLocal": "Отключено для локальных адресов",
|
"CertValidationNoLocal": "Отключено для локальных адресов",
|
||||||
"CertificateValidationHelpText": "Изменить строгое подтверждение сертификации НТТР",
|
"CertificateValidationHelpText": "Измените строгую проверку сертификации HTTPS. Не меняйте, если вы не понимаете риски.",
|
||||||
"BypassProxyForLocalAddresses": "Обход прокси для локальных адресов",
|
"BypassProxyForLocalAddresses": "Обход прокси для локальных адресов",
|
||||||
"BranchUpdateMechanism": "Ветвь, используемая внешним механизмом обновления",
|
"BranchUpdateMechanism": "Ветвь, используемая внешним механизмом обновления",
|
||||||
"BranchUpdate": "Ветвь для обновления Radarr",
|
"BranchUpdate": "Ветвь для обновления Radarr",
|
||||||
"Branch": "Ветка",
|
"Branch": "Ветка",
|
||||||
"BindAddressHelpText": "Действительный IP4-адрес или '*' для всех интерфейсов",
|
"BindAddressHelpText": "Действительный IPv4-адрес или '*' для всех интерфейсов",
|
||||||
"BackupFolderHelpText": "Относительные пути будут в каталоге AppData Radarr",
|
"BackupFolderHelpText": "Относительные пути будут в каталоге AppData Radarr",
|
||||||
"AvailabilityDelayHelpText": "Время до или после доступной даты для поиска фильма",
|
"AvailabilityDelayHelpText": "Время до или после доступной даты для поиска фильма",
|
||||||
"AvailabilityDelay": "Задержка доступности",
|
"AvailabilityDelay": "Задержка доступности",
|
||||||
@@ -908,7 +908,7 @@
|
|||||||
"TimeFormat": "Формат времени",
|
"TimeFormat": "Формат времени",
|
||||||
"Time": "Время",
|
"Time": "Время",
|
||||||
"ThisConditionMatchesUsingRegularExpressions": "Это условие соответствует использованию регулярных выражений. Обратите внимание, что символы {0} имеют особое значение и требуют экранирования с помощью {1}",
|
"ThisConditionMatchesUsingRegularExpressions": "Это условие соответствует использованию регулярных выражений. Обратите внимание, что символы {0} имеют особое значение и требуют экранирования с помощью {1}",
|
||||||
"ThisCannotBeCancelled": "Нельзя отменить после запуска без перезапуска Radarr.",
|
"ThisCannotBeCancelled": "Это действие нельзя отменить после запуска без отключения всех ваших индексаторов.",
|
||||||
"TheLogLevelDefault": "Уровень журнала по умолчанию - «Информация» и может быть изменен в",
|
"TheLogLevelDefault": "Уровень журнала по умолчанию - «Информация» и может быть изменен в",
|
||||||
"TestAllLists": "Тестировать все листы",
|
"TestAllLists": "Тестировать все листы",
|
||||||
"TestAllIndexers": "Тестировать все индексаторы",
|
"TestAllIndexers": "Тестировать все индексаторы",
|
||||||
@@ -982,7 +982,7 @@
|
|||||||
"ShowCertification": "Показать сертификаты",
|
"ShowCertification": "Показать сертификаты",
|
||||||
"ShowAsAllDayEvents": "Показывать как мероприятия на весь день",
|
"ShowAsAllDayEvents": "Показывать как мероприятия на весь день",
|
||||||
"ShowAdvanced": "Показать расширенные",
|
"ShowAdvanced": "Показать расширенные",
|
||||||
"ShouldMonitorHelpText": "Если включено, фильмы, добавленные в этот список, добавляются и отслеживаются",
|
"ShouldMonitorHelpText": "Если включено, фильмы или коллекции, добавленные в этот список, будут добавлены, как отслеживаемые",
|
||||||
"SettingsWeekColumnHeaderHelpText": "Отображается над каждым столбцом, когда неделя активна",
|
"SettingsWeekColumnHeaderHelpText": "Отображается над каждым столбцом, когда неделя активна",
|
||||||
"SettingsWeekColumnHeader": "Заголовок столбца недели",
|
"SettingsWeekColumnHeader": "Заголовок столбца недели",
|
||||||
"SettingsTimeFormat": "Формат времени",
|
"SettingsTimeFormat": "Формат времени",
|
||||||
@@ -1015,7 +1015,7 @@
|
|||||||
"Security": "Безопасность",
|
"Security": "Безопасность",
|
||||||
"Seconds": "Секунды",
|
"Seconds": "Секунды",
|
||||||
"SearchSelected": "Искать выделенные",
|
"SearchSelected": "Искать выделенные",
|
||||||
"SearchOnAddHelpText": "Искать фильмы в этом списке при добавлении в Radarr",
|
"SearchOnAddHelpText": "Искать фильмы в этом списке при добавлении в библиотеку",
|
||||||
"SearchOnAdd": "Искать при добавлении",
|
"SearchOnAdd": "Искать при добавлении",
|
||||||
"SearchMovie": "Поиск фильма",
|
"SearchMovie": "Поиск фильма",
|
||||||
"SearchMissing": "Поиск пропавших",
|
"SearchMissing": "Поиск пропавших",
|
||||||
@@ -1103,10 +1103,44 @@
|
|||||||
"ImdbVotes": "IMDb оценок",
|
"ImdbVotes": "IMDb оценок",
|
||||||
"Duration": "Длительность",
|
"Duration": "Длительность",
|
||||||
"Rating": "Рейтинг",
|
"Rating": "Рейтинг",
|
||||||
"List": "Списки",
|
"List": "Список",
|
||||||
"RssSyncHelpText": "Интервал в минутах. Установите 0 чтобы выключить (остановит все автоматические захваты релизов)",
|
"RssSyncHelpText": "Интервал в минутах. Установите 0, чтобы выключить (остановит все автоматические захваты релизов)",
|
||||||
"AllCollectionsHiddenDueToFilter": "Все фильмы спрятаны в соответствии с фильтром.",
|
"AllCollectionsHiddenDueToFilter": "Все фильмы скрыты в соответствии с фильтром.",
|
||||||
"Collections": "Коллекция",
|
"Collections": "Коллекции",
|
||||||
"MonitorMovies": "Отслеживать фильм",
|
"MonitorMovies": "Отслеживать фильм",
|
||||||
"NoCollections": "Фильмов не найдено. Для начала вам нужно добавить новый фильм или импортировать уже существующие."
|
"NoCollections": "Коллекции не найдены. Для начала вам нужно добавить новый фильм или импортировать несколько существующих.",
|
||||||
|
"CollectionOptions": "Параметры коллекции",
|
||||||
|
"CollectionShowDetailsHelpText": "Показать статус и свойства коллекции",
|
||||||
|
"CollectionShowOverviewsHelpText": "Показать обзоры коллекций",
|
||||||
|
"CollectionShowPostersHelpText": "Показать постеры предметов коллекции",
|
||||||
|
"EditCollection": "Редактировать коллекцию",
|
||||||
|
"Auto": "Авто",
|
||||||
|
"MonitorCollection": "Отслеживание коллекции",
|
||||||
|
"MonitoredCollectionHelpText": "Контролировать, чтобы фильмы из этой коллекции автоматически добавлялись в библиотеку",
|
||||||
|
"MovieAndCollection": "Фильм и коллекция",
|
||||||
|
"MovieCollectionMissingRoot": "Отсутствует корневая папка для коллекции фильмов: {0}",
|
||||||
|
"MovieCollectionMultipleMissingRoots": "Для коллекций фильмов отсутствуют несколько корневых папок: {0}",
|
||||||
|
"MovieOnly": "Только фильм",
|
||||||
|
"ShowCollectionDetails": "Показать статус коллекции",
|
||||||
|
"OnMovieAddedHelpText": "Добавлено в фильм",
|
||||||
|
"RefreshCollections": "Обновить коллекции",
|
||||||
|
"RefreshMonitoredIntervalHelpText": "Как часто обновлять отслеживаемые загрузки с клиентов загрузки, минимум 1 минута",
|
||||||
|
"Never": "Никогда",
|
||||||
|
"OriginalTitle": "Оригинальное название",
|
||||||
|
"CollectionsSelectedInterp": "{0} коллекций выбрано",
|
||||||
|
"UnableToLoadCollections": "Не удалось загрузить коллекции",
|
||||||
|
"OnMovieAdded": "Добавлено в фильм",
|
||||||
|
"OriginalLanguage": "Язык оригинала",
|
||||||
|
"SearchOnAddCollectionHelpText": "Поиск фильмов в этой коллекции при добавлении в библиотеку",
|
||||||
|
"ShowOverview": "Показать обзор",
|
||||||
|
"ShowPosters": "Показать постеры",
|
||||||
|
"TotalMovies": "Всего фильмов",
|
||||||
|
"Waiting": "Ожидание",
|
||||||
|
"ChooseImportMode": "Выберите режим импорта",
|
||||||
|
"InstanceName": "Имя экземпляра",
|
||||||
|
"InstanceNameHelpText": "Имя экземпляра на вкладке и для имени приложения системного журнала",
|
||||||
|
"RottenTomatoesRating": "Tomato рейтинг",
|
||||||
|
"Started": "Запущено",
|
||||||
|
"Database": "База данных",
|
||||||
|
"From": "из"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,64 @@
|
|||||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Naozaj chcete odobrať {0} položku {1} z fronty?",
|
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Naozaj chcete odobrať {0} položku {1} z fronty?",
|
||||||
"ImportCustomFormat": "Pridať vlastný formát",
|
"ImportCustomFormat": "Pridať vlastný formát",
|
||||||
"DeleteRestrictionHelpText": "Naozaj chcete zmazať tento profil oneskorenia?",
|
"DeleteRestrictionHelpText": "Naozaj chcete zmazať tento profil oneskorenia?",
|
||||||
"AllCollectionsHiddenDueToFilter": "Všetky filmy sú skryté kvôli použitému filtru."
|
"AllCollectionsHiddenDueToFilter": "Všetky filmy sú skryté kvôli použitému filtru.",
|
||||||
|
"CollectionShowOverviewsHelpText": "Zobraziť prehľady kolekcií",
|
||||||
|
"CollectionShowPostersHelpText": "Zobraziť plagáty kolekcií",
|
||||||
|
"CollectionOptions": "Možnosti kolekcie",
|
||||||
|
"ChmodGroup": "chmod Skupina",
|
||||||
|
"ColonReplacement": "Nahrádzanie bodkočiarky",
|
||||||
|
"CompletedDownloadHandling": "Zaobchádzanie s dokončenými sťahovaniami",
|
||||||
|
"Component": "Komponent",
|
||||||
|
"Conditions": "Podmienky",
|
||||||
|
"Connection": "Spojenie",
|
||||||
|
"ConnectionLost": "Spojenie prerušené",
|
||||||
|
"CertificateValidationHelpText": "Zmeňte, aké prísne je overenie certifikácie HTTPS. Nemeňte, pokiaľ nerozumiete rizikám.",
|
||||||
|
"Connect": "Pripojiť",
|
||||||
|
"AllowHardcodedSubs": "Povoliť pevne zakódované titulky",
|
||||||
|
"ApplicationUrlHelpText": "Externá URL tejto aplikácie vrátane http(s)://, portu a URL základu",
|
||||||
|
"CantFindMovie": "Prečo nemôžem nájsť môj film?",
|
||||||
|
"Cast": "Obsadenie",
|
||||||
|
"Certification": "Certifikácia",
|
||||||
|
"CertificationCountry": "Krajina certifikácie",
|
||||||
|
"ChangeHasNotBeenSavedYet": "Zmena ešte nebola uložená",
|
||||||
|
"ChmodGroupHelpTextWarning": "Toto funguje iba ak použivateľ bežiaci Radarr je vlastníkom súboru. Je to lepšie zaistite, že klient na sťahovanie používa tú istú skupinu ako Radarr.",
|
||||||
|
"ChmodFolderHelpTextWarning": "Toto funguje iba ak používateľ bežiaci Radarr je vlastníkom súboru. Je lepšie zaistiť, že klient na sťahovanie správne nastaví oprávnenia.",
|
||||||
|
"ChooseAnotherFolder": "Vybrať ďalší priečinok",
|
||||||
|
"ClickToChangeReleaseGroup": "Kliknutím zmeníte skupinu vydania",
|
||||||
|
"Collection": "Kolekcia",
|
||||||
|
"CollectionShowDetailsHelpText": "Zobraziť stav kolekcie a vlastnosti",
|
||||||
|
"ColonReplacementFormatHelpText": "Zmeňte spôsob, akým Radarr rieši nahradzovanie bodkočiarky",
|
||||||
|
"Columns": "Stĺpce",
|
||||||
|
"CheckDownloadClientForDetails": "ďalšie podrobnosti nájdete v klientovi na sťahovanie",
|
||||||
|
"Collections": "Kolekcie",
|
||||||
|
"CollectionsSelectedInterp": "{0} Označených kolekcií",
|
||||||
|
"AnnouncedMsg": "Film je ohlásený",
|
||||||
|
"AreYouSureYouWantToRemoveTheSelectedItemsFromBlocklist": "Naozaj si prajete odstrániť označené položky z blocklistu?",
|
||||||
|
"ChooseImportMode": "Zvoliť Import mód",
|
||||||
|
"ApplicationURL": "URL aplikácie",
|
||||||
|
"Auto": "Automaticky",
|
||||||
|
"BindAddress": "Viazať adresu",
|
||||||
|
"Blocklist": "Blocklist",
|
||||||
|
"Blocklisted": "V blockliste",
|
||||||
|
"BlocklistHelpText": "Zabráni Radarru, aby znova automaticky grabol toto vydanie",
|
||||||
|
"BlocklistRelease": "Blocklistnúť vydanie",
|
||||||
|
"CancelPendingTask": "Naozaj chcete zrušiť túto prebiehajúcu úlohu?",
|
||||||
|
"CancelProcessing": "Zrušiť spracovanie",
|
||||||
|
"CertificateValidation": "Overenie certifikátu",
|
||||||
|
"CertificationCountryHelpText": "Vyberte krajinu pre filmové certifikácie",
|
||||||
|
"CertValidationNoLocal": "Zakázané pre miestne adresy",
|
||||||
|
"ChangeFileDate": "Zmeniť dátum súboru",
|
||||||
|
"CheckForFinishedDownloadsInterval": "Interval kontroly dokončených sťahovaní",
|
||||||
|
"ChmodFolder": "chmod Priečinok",
|
||||||
|
"ChmodGroupHelpText": "Názov skupiny alebo gid. Použite gid pre vzdialené súborové systémy.",
|
||||||
|
"Clear": "Vymazať",
|
||||||
|
"ClickToChangeLanguage": "Kliknutím zmeníte jazyk",
|
||||||
|
"ClickToChangeQuality": "Kliknutím zmeníte kvalitu",
|
||||||
|
"ClientPriority": "Priorita klienta",
|
||||||
|
"CloneCustomFormat": "Klonovať vlastný formát",
|
||||||
|
"CloneFormatTag": "Klonovať značku formátu",
|
||||||
|
"CloneIndexer": "Klonovať indexer",
|
||||||
|
"CloneProfile": "Klonovať profil",
|
||||||
|
"Close": "Zatvoriť",
|
||||||
|
"CloseCurrentModal": "Zatvoriť aktuálne okno"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -637,7 +637,7 @@
|
|||||||
"InstallLatest": "安装最新版",
|
"InstallLatest": "安装最新版",
|
||||||
"IndexerStatusCheckSingleClientMessage": "搜刮器因错误不可用:{0}",
|
"IndexerStatusCheckSingleClientMessage": "搜刮器因错误不可用:{0}",
|
||||||
"IndexerStatusCheckAllClientMessage": "所有搜刮器都因错误不可用",
|
"IndexerStatusCheckAllClientMessage": "所有搜刮器都因错误不可用",
|
||||||
"IndexerSearchCheckNoInteractiveMessage": "没有任何索引器开启了手动搜索,Radarr 不会提供任何手动搜索结果",
|
"IndexerSearchCheckNoInteractiveMessage": "没有任何索引器开启了手动搜索,因此Radarr 不会提供任何手动搜索的结果",
|
||||||
"IndexerRssHealthCheckNoIndexers": "没有任何索引器开启了RSS同步,Radarr不会自动抓取新发布的影片",
|
"IndexerRssHealthCheckNoIndexers": "没有任何索引器开启了RSS同步,Radarr不会自动抓取新发布的影片",
|
||||||
"IndexerLongTermStatusCheckSingleClientMessage": "由于故障6小时,下列索引器都已不可用:{0}",
|
"IndexerLongTermStatusCheckSingleClientMessage": "由于故障6小时,下列索引器都已不可用:{0}",
|
||||||
"IndexerLongTermStatusCheckAllClientMessage": "由于故障超过6小时,所有索引器均不可用",
|
"IndexerLongTermStatusCheckAllClientMessage": "由于故障超过6小时,所有索引器均不可用",
|
||||||
@@ -740,7 +740,7 @@
|
|||||||
"UnableToAddANewNotificationPleaseTryAgain": "无法添加新通知,请稍后重试。",
|
"UnableToAddANewNotificationPleaseTryAgain": "无法添加新通知,请稍后重试。",
|
||||||
"URLBase": "基本URL",
|
"URLBase": "基本URL",
|
||||||
"RemovedMovieCheckMultipleMessage": "电影“{0}”已从TMDb移除",
|
"RemovedMovieCheckMultipleMessage": "电影“{0}”已从TMDb移除",
|
||||||
"SkipFreeSpaceCheckWhenImportingHelpText": "当 Radarr 无法从您的电影根文件夹中检测到空闲空间时使用",
|
"SkipFreeSpaceCheckWhenImportingHelpText": "当 Radarr 无法从movie根目录检测到空间时使用",
|
||||||
"UnableToAddANewQualityProfilePleaseTryAgain": "无法添加新影片质量配置,请稍后重试。",
|
"UnableToAddANewQualityProfilePleaseTryAgain": "无法添加新影片质量配置,请稍后重试。",
|
||||||
"ThisCannotBeCancelled": "在不禁用所有索引器的情况下,一旦启动就无法取消。",
|
"ThisCannotBeCancelled": "在不禁用所有索引器的情况下,一旦启动就无法取消。",
|
||||||
"SearchCutoffUnmet": "搜索未满足终止条件的",
|
"SearchCutoffUnmet": "搜索未满足终止条件的",
|
||||||
@@ -1135,5 +1135,13 @@
|
|||||||
"NoCollections": "没有发现集合,点击添加新的电影或者导入已经存在的电影",
|
"NoCollections": "没有发现集合,点击添加新的电影或者导入已经存在的电影",
|
||||||
"InstanceNameHelpText": "选项卡及日志应用名称",
|
"InstanceNameHelpText": "选项卡及日志应用名称",
|
||||||
"ScrollMovies": "滚动电影",
|
"ScrollMovies": "滚动电影",
|
||||||
"CollectionOptions": "Collection Options"
|
"CollectionOptions": "集选项Collection Options",
|
||||||
|
"CollectionShowDetailsHelpText": "显示集合collection的状态和属性",
|
||||||
|
"CollectionShowOverviewsHelpText": "显示集合collection的概述",
|
||||||
|
"CollectionShowPostersHelpText": "显示集合项目海报",
|
||||||
|
"RottenTomatoesRating": "烂番茄指数",
|
||||||
|
"ShowPosters": "显示海报",
|
||||||
|
"OnMovieAdded": "电影添加时",
|
||||||
|
"OnMovieAddedHelpText": "电影添加时",
|
||||||
|
"TotalMovies": "电影总数"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,18 @@
|
|||||||
{
|
{
|
||||||
"About": "關於",
|
"About": "關於",
|
||||||
"Add": "添加",
|
"Add": "新增",
|
||||||
"AcceptConfirmationModal": "接受確認模式",
|
"AcceptConfirmationModal": "接受確認模式",
|
||||||
"Actions": "行動",
|
"Actions": "執行",
|
||||||
"Activity": "活動"
|
"Activity": "活動",
|
||||||
|
"AddIndexer": "新增索引",
|
||||||
|
"AddingTag": "新增標籤",
|
||||||
|
"AddDownloadClient": "新增下載器",
|
||||||
|
"Added": "已新增",
|
||||||
|
"Age": "年齡",
|
||||||
|
"All": "全部",
|
||||||
|
"Analytics": "分析",
|
||||||
|
"AddCustomFormat": "新增自定義格式",
|
||||||
|
"AddList": "新增列表",
|
||||||
|
"AddDelayProfile": "新增延時配置",
|
||||||
|
"AddedToDownloadQueue": "已添加到下載隊列"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.Download;
|
using NzbDrone.Core.Download;
|
||||||
@@ -260,6 +261,20 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var extension = Path.GetExtension(fileInfo.Name);
|
||||||
|
|
||||||
|
if (extension.IsNullOrWhiteSpace() || !MediaFileExtensions.Extensions.Contains(extension))
|
||||||
|
{
|
||||||
|
_logger.Debug("[{0}] has an unsupported extension: '{1}'", fileInfo.FullName, extension);
|
||||||
|
|
||||||
|
return new List<ImportResult>
|
||||||
|
{
|
||||||
|
new ImportResult(new ImportDecision(new LocalMovie { Path = fileInfo.FullName },
|
||||||
|
new Rejection($"Invalid video file, unsupported extension: '{extension}'")),
|
||||||
|
$"Invalid video file, unsupported extension: '{extension}'")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (downloadClientItem == null)
|
if (downloadClientItem == null)
|
||||||
{
|
{
|
||||||
if (_diskProvider.IsFileLocked(fileInfo.FullName))
|
if (_diskProvider.IsFileLocked(fileInfo.FullName))
|
||||||
|
|||||||
@@ -344,7 +344,16 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
|||||||
yearStr = $" {movie.Year}";
|
yearStr = $" {movie.Year}";
|
||||||
}
|
}
|
||||||
|
|
||||||
newMovie = SearchForNewMovie(movie.Title + yearStr).FirstOrDefault()?.MovieMetadata ?? null;
|
var newMovieObject = SearchForNewMovie(movie.Title + yearStr).FirstOrDefault();
|
||||||
|
|
||||||
|
if (newMovieObject == null)
|
||||||
|
{
|
||||||
|
newMovie = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newMovie = newMovieObject.MovieMetadata;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newMovie == null)
|
if (newMovie == null)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ namespace NzbDrone.Core.Movies
|
|||||||
_diskProvider.CreateFolder(new DirectoryInfo(destinationPath).Parent.FullName);
|
_diskProvider.CreateFolder(new DirectoryInfo(destinationPath).Parent.FullName);
|
||||||
_diskTransferService.TransferFolder(sourcePath, destinationPath, TransferMode.Move);
|
_diskTransferService.TransferFolder(sourcePath, destinationPath, TransferMode.Move);
|
||||||
|
|
||||||
_logger.ProgressInfo("{0} moved successfully to {1}", movie.Title, movie.Path);
|
_logger.ProgressInfo("{0} moved successfully to {1}", movie.Title, destinationPath);
|
||||||
|
|
||||||
_eventAggregator.PublishEvent(new MovieMovedEvent(movie, sourcePath, destinationPath));
|
_eventAggregator.PublishEvent(new MovieMovedEvent(movie, sourcePath, destinationPath));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ namespace NzbDrone.Core.Movies
|
|||||||
{
|
{
|
||||||
using (var conn = _database.OpenConnection())
|
using (var conn = _database.OpenConnection())
|
||||||
{
|
{
|
||||||
var strSql = "SELECT \"Id\" AS \"Key\", \"Tags\" AS \"Value\" FROM \"Movies\"";
|
var strSql = "SELECT \"Id\" AS \"Key\", \"Tags\" AS \"Value\" FROM \"Movies\" WHERE \"Tags\" IS NOT NULL";
|
||||||
return conn.Query<KeyValuePair<int, List<int>>>(strSql).ToDictionary(x => x.Key, x => x.Value);
|
return conn.Query<KeyValuePair<int, List<int>>>(strSql).ToDictionary(x => x.Key, x => x.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,10 +119,18 @@ namespace NzbDrone.Core.Movies
|
|||||||
return FindByTitle(new List<string> { title }, year, otherTitles, candidates);
|
return FindByTitle(new List<string> { title }, year, otherTitles, candidates);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Movie FindByTitle(List<string> cleanTitles, int? year, List<string> otherTitles, List<Movie> candidates)
|
public Movie FindByTitle(List<string> titles, int? year, List<string> otherTitles, List<Movie> candidates)
|
||||||
{
|
{
|
||||||
|
var cleanTitles = titles.Select(t => t.CleanMovieTitle().ToLowerInvariant());
|
||||||
|
|
||||||
var result = candidates.Where(x => cleanTitles.Contains(x.MovieMetadata.Value.CleanTitle)).FirstWithYear(year);
|
var result = candidates.Where(x => cleanTitles.Contains(x.MovieMetadata.Value.CleanTitle)).FirstWithYear(year);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
result =
|
||||||
|
candidates.Where(movie => cleanTitles.Contains(movie.MovieMetadata.Value.CleanOriginalTitle)).FirstWithYear(year);
|
||||||
|
}
|
||||||
|
|
||||||
if (result == null)
|
if (result == null)
|
||||||
{
|
{
|
||||||
result =
|
result =
|
||||||
|
|||||||
@@ -98,6 +98,14 @@ namespace NzbDrone.Core.Notifications.Discord
|
|||||||
discordField.Name = "Links";
|
discordField.Name = "Links";
|
||||||
discordField.Value = GetLinksString(message.Movie);
|
discordField.Value = GetLinksString(message.Movie);
|
||||||
break;
|
break;
|
||||||
|
case DiscordGrabFieldType.CustomFormats:
|
||||||
|
discordField.Name = "Custom Formats";
|
||||||
|
discordField.Value = string.Join("|", message.RemoteMovie.CustomFormats);
|
||||||
|
break;
|
||||||
|
case DiscordGrabFieldType.CustomFormatScore:
|
||||||
|
discordField.Name = "Custom Format Score";
|
||||||
|
discordField.Value = message.RemoteMovie.CustomFormatScore.ToString();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discordField.Name.IsNotNullOrWhiteSpace() && discordField.Value.IsNotNullOrWhiteSpace())
|
if (discordField.Name.IsNotNullOrWhiteSpace() && discordField.Value.IsNotNullOrWhiteSpace())
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ namespace NzbDrone.Core.Notifications.Discord
|
|||||||
Links,
|
Links,
|
||||||
Release,
|
Release,
|
||||||
Poster,
|
Poster,
|
||||||
Fanart
|
Fanart,
|
||||||
|
CustomFormats,
|
||||||
|
CustomFormatScore
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DiscordImportFieldType
|
public enum DiscordImportFieldType
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace NzbDrone.Core.Notifications.Notifiarr
|
|
||||||
{
|
|
||||||
public enum NotifiarrEnvironment
|
|
||||||
{
|
|
||||||
Live,
|
|
||||||
Development
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -79,7 +79,7 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var url = settings.Environment == (int)NotifiarrEnvironment.Development ? "https://dev.notifiarr.com" : "https://notifiarr.com";
|
var url = "https://notifiarr.com";
|
||||||
var requestBuilder = new HttpRequestBuilder(url + "/api/v1/notification/radarr/" + settings.APIKey).Post();
|
var requestBuilder = new HttpRequestBuilder(url + "/api/v1/notification/radarr/" + settings.APIKey).Post();
|
||||||
requestBuilder.AddFormParameter("instanceName", settings.InstanceName).Build();
|
requestBuilder.AddFormParameter("instanceName", settings.InstanceName).Build();
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
|||||||
public string APIKey { get; set; }
|
public string APIKey { get; set; }
|
||||||
[FieldDefinition(1, Label = "Instance Name", Advanced = true, HelpText = "Unique name for this instance", HelpLink = "https://notifiarr.com")]
|
[FieldDefinition(1, Label = "Instance Name", Advanced = true, HelpText = "Unique name for this instance", HelpLink = "https://notifiarr.com")]
|
||||||
public string InstanceName { get; set; }
|
public string InstanceName { get; set; }
|
||||||
[FieldDefinition(2, Label = "Environment", Advanced = true, Type = FieldType.Select, SelectOptions = typeof(NotifiarrEnvironment), HelpText = "Live unless told otherwise", HelpLink = "https://notifiarr.com")]
|
|
||||||
public int Environment { get; set; }
|
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
|
|||||||
66
src/NzbDrone.Core/Notifications/Ntfy/Ntfy.cs
Normal file
66
src/NzbDrone.Core/Notifications/Ntfy/Ntfy.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Movies;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Ntfy
|
||||||
|
{
|
||||||
|
public class Ntfy : NotificationBase<NtfySettings>
|
||||||
|
{
|
||||||
|
private readonly INtfyProxy _proxy;
|
||||||
|
|
||||||
|
public Ntfy(INtfyProxy proxy)
|
||||||
|
{
|
||||||
|
_proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "ntfy.sh";
|
||||||
|
|
||||||
|
public override string Link => "https://ntfy.sh/";
|
||||||
|
|
||||||
|
public override void OnGrab(GrabMessage grabMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(MOVIE_GRABBED_TITLE_BRANDED, grabMessage.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDownload(DownloadMessage message)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(MOVIE_DOWNLOADED_TITLE_BRANDED, message.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMovieAdded(Movie movie)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(MOVIE_ADDED_TITLE_BRANDED, $"{movie.Title} added to library", Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMovieFileDelete(MovieFileDeleteMessage deleteMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(MOVIE_FILE_DELETED_TITLE, deleteMessage.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMovieDelete(MovieDeleteMessage deleteMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(MOVIE_DELETED_TITLE, deleteMessage.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResult Test()
|
||||||
|
{
|
||||||
|
var failures = new List<ValidationFailure>();
|
||||||
|
|
||||||
|
failures.AddIfNotNull(_proxy.Test(Settings));
|
||||||
|
|
||||||
|
return new ValidationResult(failures);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/NzbDrone.Core/Notifications/Ntfy/NtfyException.cs
Normal file
18
src/NzbDrone.Core/Notifications/Ntfy/NtfyException.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using NzbDrone.Common.Exceptions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Ntfy
|
||||||
|
{
|
||||||
|
public class NtfyException : NzbDroneException
|
||||||
|
{
|
||||||
|
public NtfyException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public NtfyException(string message, Exception innerException, params object[] args)
|
||||||
|
: base(message, innerException, args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/NzbDrone.Core/Notifications/Ntfy/NtfyPriority.cs
Normal file
11
src/NzbDrone.Core/Notifications/Ntfy/NtfyPriority.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace NzbDrone.Core.Notifications.Ntfy
|
||||||
|
{
|
||||||
|
public enum NtfyPriority
|
||||||
|
{
|
||||||
|
Min = 1,
|
||||||
|
Low = 2,
|
||||||
|
Default = 3,
|
||||||
|
High = 4,
|
||||||
|
Max = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
138
src/NzbDrone.Core/Notifications/Ntfy/NtfyProxy.cs
Normal file
138
src/NzbDrone.Core/Notifications/Ntfy/NtfyProxy.cs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Ntfy
|
||||||
|
{
|
||||||
|
public interface INtfyProxy
|
||||||
|
{
|
||||||
|
void SendNotification(string title, string message, NtfySettings settings);
|
||||||
|
|
||||||
|
ValidationFailure Test(NtfySettings settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NtfyProxy : INtfyProxy
|
||||||
|
{
|
||||||
|
private const string DEFAULT_PUSH_URL = "https://ntfy.sh";
|
||||||
|
|
||||||
|
private readonly IHttpClient _httpClient;
|
||||||
|
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public NtfyProxy(IHttpClient httpClient, Logger logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendNotification(string title, string message, NtfySettings settings)
|
||||||
|
{
|
||||||
|
var error = false;
|
||||||
|
|
||||||
|
var serverUrl = settings.ServerUrl.IsNullOrWhiteSpace() ? NtfyProxy.DEFAULT_PUSH_URL : settings.ServerUrl;
|
||||||
|
|
||||||
|
foreach (var topic in settings.Topics)
|
||||||
|
{
|
||||||
|
var request = BuildTopicRequest(serverUrl, topic);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SendNotification(title, message, request, settings);
|
||||||
|
}
|
||||||
|
catch (NtfyException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Unable to send test message to {0}", topic);
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
throw new NtfyException("Unable to send Ntfy notifications to all topics");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpRequestBuilder BuildTopicRequest(string serverUrl, string topic)
|
||||||
|
{
|
||||||
|
var trimServerUrl = serverUrl.TrimEnd('/');
|
||||||
|
|
||||||
|
var requestBuilder = new HttpRequestBuilder($"{trimServerUrl}/{topic}").Post();
|
||||||
|
|
||||||
|
return requestBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationFailure Test(NtfySettings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const string title = "Radarr - Test Notification";
|
||||||
|
|
||||||
|
const string body = "This is a test message from Radarr";
|
||||||
|
|
||||||
|
SendNotification(title, body, settings);
|
||||||
|
}
|
||||||
|
catch (HttpException ex)
|
||||||
|
{
|
||||||
|
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized || ex.Response.StatusCode == HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Authorization is required");
|
||||||
|
return new ValidationFailure("UserName", "Authorization is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Error(ex, "Unable to send test message");
|
||||||
|
return new ValidationFailure("ServerUrl", "Unable to send test message");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Unable to send test message");
|
||||||
|
return new ValidationFailure("", "Unable to send test message");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendNotification(string title, string message, HttpRequestBuilder requestBuilder, NtfySettings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
requestBuilder.Headers.Add("X-Title", title);
|
||||||
|
requestBuilder.Headers.Add("X-Message", message);
|
||||||
|
requestBuilder.Headers.Add("X-Priority", settings.Priority.ToString());
|
||||||
|
|
||||||
|
if (settings.Tags.Any())
|
||||||
|
{
|
||||||
|
requestBuilder.Headers.Add("X-Tags", settings.Tags.Join(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.ClickUrl.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
requestBuilder.Headers.Add("X-Click", settings.ClickUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = requestBuilder.Build();
|
||||||
|
|
||||||
|
if (!settings.UserName.IsNullOrWhiteSpace() && !settings.Password.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
request.Credentials = new BasicNetworkCredential(settings.UserName, settings.Password);
|
||||||
|
}
|
||||||
|
|
||||||
|
_httpClient.Execute(request);
|
||||||
|
}
|
||||||
|
catch (HttpException ex)
|
||||||
|
{
|
||||||
|
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized || ex.Response.StatusCode == HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Authorization is required");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NtfyException("Unable to send text message: {0}", ex, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/NzbDrone.Core/Notifications/Ntfy/NtfySettings.cs
Normal file
63
src/NzbDrone.Core/Notifications/Ntfy/NtfySettings.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Ntfy
|
||||||
|
{
|
||||||
|
public class NtfySettingsValidator : AbstractValidator<NtfySettings>
|
||||||
|
{
|
||||||
|
public NtfySettingsValidator()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.Topics).NotEmpty();
|
||||||
|
RuleFor(c => c.Priority).InclusiveBetween(1, 5);
|
||||||
|
RuleFor(c => c.ServerUrl).IsValidUrl().When(c => !c.ServerUrl.IsNullOrWhiteSpace());
|
||||||
|
RuleFor(c => c.ClickUrl).IsValidUrl().When(c => !c.ClickUrl.IsNullOrWhiteSpace());
|
||||||
|
RuleFor(c => c.UserName).NotEmpty().When(c => !c.Password.IsNullOrWhiteSpace());
|
||||||
|
RuleFor(c => c.Password).NotEmpty().When(c => !c.UserName.IsNullOrWhiteSpace());
|
||||||
|
RuleForEach(c => c.Topics).NotEmpty().Matches("[a-zA-Z0-9_-]+").Must(c => !InvalidTopics.Contains(c)).WithMessage("Invalid topic");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> InvalidTopics => new List<string> { "announcements", "app", "docs", "settings", "stats", "mytopic-rw", "mytopic-ro", "mytopic-wo" };
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NtfySettings : IProviderConfig
|
||||||
|
{
|
||||||
|
private static readonly NtfySettingsValidator Validator = new NtfySettingsValidator();
|
||||||
|
|
||||||
|
public NtfySettings()
|
||||||
|
{
|
||||||
|
Topics = Array.Empty<string>();
|
||||||
|
Priority = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "Server Url", Type = FieldType.Url, HelpLink = "https://ntfy.sh/docs/install/", HelpText = "Leave blank to use public server (https://ntfy.sh)", Placeholder = "https://ntfy.sh")]
|
||||||
|
public string ServerUrl { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "User Name", HelpText = "Optional Authorization", Privacy = PrivacyLevel.UserName)]
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Optional Password", Privacy = PrivacyLevel.Password)]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(3, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(NtfyPriority))]
|
||||||
|
public int Priority { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(4, Label = "Topics", HelpText = "List of Topics to send notifications to", Type = FieldType.Tag, Placeholder = "Topic1234,Topic4321")]
|
||||||
|
public IEnumerable<string> Topics { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(5, Label = "Ntfy Tags and Emojis", Type = FieldType.Tag, HelpText = "Optional list of tags or emojis to use", Placeholder = "warning,skull", HelpLink = "https://ntfy.sh/docs/emojis/")]
|
||||||
|
public IEnumerable<string> Tags { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(6, Label = "Click Url", Type = FieldType.Url, HelpText = "Optional link when user clicks notification", Placeholder = "https://myserver.example.com/radarr")]
|
||||||
|
public string ClickUrl { get; set; }
|
||||||
|
|
||||||
|
public NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user