mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-09 15:01:39 -04:00
Compare commits
26 Commits
update-con
...
list-exclu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74382d7250 | ||
|
|
6659bc034c | ||
|
|
7404793dcf | ||
|
|
d8af17ce3d | ||
|
|
44c912f02d | ||
|
|
b104368e23 | ||
|
|
aa0104b6bc | ||
|
|
69fcd8ec94 | ||
|
|
a59928c66a | ||
|
|
1cb7ae11a2 | ||
|
|
ca519047dd | ||
|
|
f15a6abde0 | ||
|
|
2aacebc938 | ||
|
|
120e9b673e | ||
|
|
0a77a13fa8 | ||
|
|
383f9647c3 | ||
|
|
7f7c672b93 | ||
|
|
2690ad8fe1 | ||
|
|
801204b6de | ||
|
|
cb9514abaf | ||
|
|
fd22cb44f6 | ||
|
|
2d68716376 | ||
|
|
b97e76c8b8 | ||
|
|
bfad4a8cd1 | ||
|
|
61f05710f5 | ||
|
|
a8ecefd91f |
@@ -173,7 +173,6 @@ stages:
|
||||
key: 'yarn | "$(osName)" | yarn.lock'
|
||||
restoreKeys: |
|
||||
yarn | "$(osName)"
|
||||
yarn
|
||||
path: $(yarnCacheFolder)
|
||||
displayName: Cache Yarn packages
|
||||
- bash: ./build.sh --frontend
|
||||
@@ -544,10 +543,10 @@ stages:
|
||||
variables:
|
||||
pattern: 'Radarr.*.linux-core-x64.tar.gz'
|
||||
artifactName: linux-x64-tests
|
||||
Radarr__PostgresHost: 'localhost'
|
||||
Radarr__PostgresPort: '5432'
|
||||
Radarr__PostgresUser: 'radarr'
|
||||
Radarr__PostgresPassword: 'radarr'
|
||||
Radarr__Postgres__Host: 'localhost'
|
||||
Radarr__Postgres__Port: '5432'
|
||||
Radarr__Postgres__User: 'radarr'
|
||||
Radarr__Postgres__Password: 'radarr'
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
@@ -681,10 +680,10 @@ stages:
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
variables:
|
||||
pattern: 'Radarr.*.linux-core-x64.tar.gz'
|
||||
Radarr__PostgresHost: 'localhost'
|
||||
Radarr__PostgresPort: '5432'
|
||||
Radarr__PostgresUser: 'radarr'
|
||||
Radarr__PostgresPassword: 'radarr'
|
||||
Radarr__Postgres__Host: 'localhost'
|
||||
Radarr__Postgres__Port: '5432'
|
||||
Radarr__Postgres__User: 'radarr'
|
||||
Radarr__Postgres__Password: 'radarr'
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
@@ -976,7 +975,6 @@ stages:
|
||||
key: 'yarn | "$(osName)" | yarn.lock'
|
||||
restoreKeys: |
|
||||
yarn | "$(osName)"
|
||||
yarn
|
||||
path: $(yarnCacheFolder)
|
||||
displayName: Cache Yarn packages
|
||||
- bash: ./build.sh --lint
|
||||
|
||||
@@ -181,12 +181,13 @@ class Blocklist extends Component {
|
||||
>
|
||||
<TableBody>
|
||||
{
|
||||
items.map((item) => {
|
||||
items.map((item, index) => {
|
||||
return (
|
||||
<BlocklistRowConnector
|
||||
key={item.id}
|
||||
isSelected={selectedState[item.id] || false}
|
||||
columns={columns}
|
||||
index={index}
|
||||
{...item}
|
||||
onSelectedChange={this.onSelectedChange}
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import HeartRating from 'Components/HeartRating';
|
||||
import Icon from 'Components/Icon';
|
||||
import Label from 'Components/Label';
|
||||
import Link from 'Components/Link/Link';
|
||||
import TmdbRating from 'Components/TmdbRating';
|
||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
||||
import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
|
||||
import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks';
|
||||
@@ -190,7 +190,7 @@ class AddNewMovieSearchResult extends Component {
|
||||
|
||||
<div>
|
||||
<Label size={sizes.LARGE}>
|
||||
<HeartRating
|
||||
<TmdbRating
|
||||
ratings={ratings}
|
||||
iconSize={13}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
.movie {
|
||||
.container {
|
||||
display: flex;
|
||||
padding: 10px 20px;
|
||||
width: 100%;
|
||||
|
||||
@@ -6,3 +7,19 @@
|
||||
background-color: $menuItemHoverBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
.movie {
|
||||
flex: 1 0 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tmdbLink {
|
||||
composes: link from '~Components/Link/Link.css';
|
||||
|
||||
margin-left: auto;
|
||||
color: $textColor;
|
||||
}
|
||||
|
||||
.tmdbLinkIcon {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import Link from 'Components/Link/Link';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import ImportMovieTitle from './ImportMovieTitle';
|
||||
import styles from './ImportMovieSearchResult.css';
|
||||
|
||||
@@ -18,6 +20,7 @@ class ImportMovieSearchResult extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
tmdbId,
|
||||
title,
|
||||
year,
|
||||
studio,
|
||||
@@ -25,17 +28,30 @@ class ImportMovieSearchResult extends Component {
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={styles.movie}
|
||||
onPress={this.onPress}
|
||||
>
|
||||
<ImportMovieTitle
|
||||
title={title}
|
||||
year={year}
|
||||
network={studio}
|
||||
isExistingMovie={isExistingMovie}
|
||||
/>
|
||||
</Link>
|
||||
<div className={styles.container}>
|
||||
<Link
|
||||
className={styles.movie}
|
||||
onPress={this.onPress}
|
||||
>
|
||||
<ImportMovieTitle
|
||||
title={title}
|
||||
year={year}
|
||||
network={studio}
|
||||
isExistingMovie={isExistingMovie}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
className={styles.tmdbLink}
|
||||
to={`https://www.themoviedb.org/movie/${tmdbId}`}
|
||||
>
|
||||
<Icon
|
||||
className={styles.tmdbLinkIcon}
|
||||
name={icons.EXTERNAL_LINK}
|
||||
size={16}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import AvailabilitySelectInput from 'Components/Form/AvailabilitySelectInput';
|
||||
import QualityProfileSelectInputConnector from 'Components/Form/QualityProfileSelectInputConnector';
|
||||
import RootFolderSelectInputConnector from 'Components/Form/RootFolderSelectInputConnector';
|
||||
import SelectInput from 'Components/Form/SelectInput';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import PageContentFooter from 'Components/Page/PageContentFooter';
|
||||
@@ -22,6 +25,9 @@ class CollectionFooter extends Component {
|
||||
this.state = {
|
||||
monitor: NO_CHANGE,
|
||||
monitored: NO_CHANGE,
|
||||
qualityProfileId: NO_CHANGE,
|
||||
minimumAvailability: NO_CHANGE,
|
||||
rootFolderPath: NO_CHANGE,
|
||||
destinationRootFolder: null
|
||||
};
|
||||
}
|
||||
@@ -36,7 +42,10 @@ class CollectionFooter extends Component {
|
||||
if (prevProps.isSaving && !isSaving && !saveError) {
|
||||
this.setState({
|
||||
monitored: NO_CHANGE,
|
||||
monitor: NO_CHANGE
|
||||
monitor: NO_CHANGE,
|
||||
qualityProfileId: NO_CHANGE,
|
||||
rootFolderPath: NO_CHANGE,
|
||||
minimumAvailability: NO_CHANGE
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,7 +64,9 @@ class CollectionFooter extends Component {
|
||||
onUpdateSelectedPress = () => {
|
||||
const {
|
||||
monitor,
|
||||
monitored
|
||||
monitored,
|
||||
qualityProfileId,
|
||||
minimumAvailability
|
||||
} = this.state;
|
||||
|
||||
const changes = {};
|
||||
@@ -68,6 +79,14 @@ class CollectionFooter extends Component {
|
||||
changes.monitor = monitor;
|
||||
}
|
||||
|
||||
if (qualityProfileId !== NO_CHANGE) {
|
||||
changes.qualityProfileId = qualityProfileId;
|
||||
}
|
||||
|
||||
if (minimumAvailability !== NO_CHANGE) {
|
||||
changes.minimumAvailability = minimumAvailability;
|
||||
}
|
||||
|
||||
this.props.onUpdateSelectedPress(changes);
|
||||
};
|
||||
|
||||
@@ -82,7 +101,10 @@ class CollectionFooter extends Component {
|
||||
|
||||
const {
|
||||
monitored,
|
||||
monitor
|
||||
monitor,
|
||||
qualityProfileId,
|
||||
minimumAvailability,
|
||||
rootFolderPath
|
||||
} = this.state;
|
||||
|
||||
const monitoredOptions = [
|
||||
@@ -125,6 +147,52 @@ class CollectionFooter extends Component {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
<CollectionFooterLabel
|
||||
label={translate('QualityProfile')}
|
||||
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
<QualityProfileSelectInputConnector
|
||||
name="qualityProfileId"
|
||||
value={qualityProfileId}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
<CollectionFooterLabel
|
||||
label={translate('MinimumAvailability')}
|
||||
isSaving={isSaving && minimumAvailability !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
<AvailabilitySelectInput
|
||||
name="minimumAvailability"
|
||||
value={minimumAvailability}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
<CollectionFooterLabel
|
||||
label={translate('RootFolder')}
|
||||
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
<RootFolderSelectInputConnector
|
||||
name="rootFolderPath"
|
||||
value={rootFolderPath}
|
||||
includeNoChange={true}
|
||||
isDisabled={!selectedCount}
|
||||
selectedValueOptions={{ includeFreeSpace: false }}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.buttonContainer}>
|
||||
<div className={styles.buttonContainerContent}>
|
||||
<CollectionFooterLabel
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
.image {
|
||||
align-content: center;
|
||||
margin-right: 5px;
|
||||
vertical-align: -0.125em;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -22,11 +22,11 @@ class ImdbRating extends PureComponent {
|
||||
let ratingString = '0.0';
|
||||
|
||||
if (rating) {
|
||||
ratingString = `${rating.value}`;
|
||||
ratingString = `${rating.value.toFixed(1)}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<span title={`${rating.votes} votes`}>
|
||||
<span title={`${rating ? rating.votes : 0} votes`}>
|
||||
{
|
||||
!hideIcon &&
|
||||
<img
|
||||
|
||||
@@ -21,9 +21,11 @@ class RottenTomatoRating extends PureComponent {
|
||||
const rating = ratings.rottenTomatoes;
|
||||
|
||||
let ratingString = '0%';
|
||||
let ratingImage = rtFresh;
|
||||
|
||||
if (rating) {
|
||||
ratingString = `${rating.value}%`;
|
||||
ratingImage = rating.value > 50 ? rtFresh : rtRotten;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -32,7 +34,7 @@ class RottenTomatoRating extends PureComponent {
|
||||
!hideIcon &&
|
||||
<img
|
||||
className={styles.image}
|
||||
src={rating.value > 50 ? rtFresh : rtRotten}
|
||||
src={ratingImage}
|
||||
style={{
|
||||
height: `${iconSize}px`
|
||||
}}
|
||||
|
||||
@@ -22,7 +22,7 @@ class TmdbRating extends PureComponent {
|
||||
let ratingString = '0%';
|
||||
|
||||
if (rating) {
|
||||
ratingString = `${rating.value * 10}%`;
|
||||
ratingString = `${(rating.value * 10).toFixed()}%`;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import HeartRating from 'Components/HeartRating';
|
||||
import TmdbRating from 'Components/TmdbRating';
|
||||
import { getMovieStatusDetails } from 'Movie/MovieStatus';
|
||||
import formatRuntime from 'Utilities/Date/formatRuntime';
|
||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||
@@ -111,7 +111,7 @@ function DiscoverMoviePosterInfo(props) {
|
||||
if (sortKey === 'ratings' && ratings) {
|
||||
return (
|
||||
<div className={styles.info}>
|
||||
<HeartRating
|
||||
<TmdbRating
|
||||
ratings={ratings}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import HeartRating from 'Components/HeartRating';
|
||||
import Icon from 'Components/Icon';
|
||||
import ImportListListConnector from 'Components/ImportListListConnector';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
@@ -8,6 +7,7 @@ import Link from 'Components/Link/Link';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
||||
import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
|
||||
import TmdbRating from 'Components/TmdbRating';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import AddNewDiscoverMovieModal from 'DiscoverMovie/AddNewDiscoverMovieModal';
|
||||
import ExcludeMovieModal from 'DiscoverMovie/Exclusion/ExcludeMovieModal';
|
||||
@@ -245,7 +245,7 @@ class DiscoverMovieRow extends Component {
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
<HeartRating
|
||||
<TmdbRating
|
||||
ratings={ratings}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
|
||||
@@ -102,12 +102,21 @@ function MovieIndexSortMenu(props) {
|
||||
</SortMenuItem>
|
||||
|
||||
<SortMenuItem
|
||||
name="ratings"
|
||||
name="imdbRating"
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
onPress={onSortSelect}
|
||||
>
|
||||
{translate('Ratings')}
|
||||
{translate('ImdbRating')}
|
||||
</SortMenuItem>
|
||||
|
||||
<SortMenuItem
|
||||
name="tmdbRating"
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
onPress={onSortSelect}
|
||||
>
|
||||
{translate('TmdbRating')}
|
||||
</SortMenuItem>
|
||||
|
||||
<SortMenuItem
|
||||
|
||||
@@ -77,7 +77,9 @@
|
||||
flex: 0 0 120px;
|
||||
}
|
||||
|
||||
.ratings {
|
||||
.imdbRating,
|
||||
.tmdbRating,
|
||||
.rottenTomatoesRating {
|
||||
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 0 80px;
|
||||
|
||||
@@ -84,7 +84,9 @@
|
||||
flex: 0 0 120px;
|
||||
}
|
||||
|
||||
.ratings {
|
||||
.imdbRating,
|
||||
.tmdbRating,
|
||||
.rottenTomatoesRating {
|
||||
composes: cell;
|
||||
|
||||
flex: 0 0 80px;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import HeartRating from 'Components/HeartRating';
|
||||
import Icon from 'Components/Icon';
|
||||
import ImdbRating from 'Components/ImdbRating';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||
import RottenTomatoRating from 'Components/RottenTomatoRating';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
||||
import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
|
||||
import TagListConnector from 'Components/TagListConnector';
|
||||
import TmdbRating from 'Components/TmdbRating';
|
||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import DeleteMovieModal from 'Movie/Delete/DeleteMovieModal';
|
||||
@@ -349,13 +351,39 @@ class MovieIndexRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'ratings') {
|
||||
if (name === 'tmdbRating') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
<HeartRating
|
||||
<TmdbRating
|
||||
ratings={ratings}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'rottenTomatoesRating') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
<RottenTomatoRating
|
||||
ratings={ratings}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'imdbRating') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
<ImdbRating
|
||||
ratings={ratings}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
|
||||
.tmdbId,
|
||||
.movieYear {
|
||||
flex: 0 0 70px;
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex: 1 0 auto;
|
||||
padding-right: 10px;
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import Link from 'Components/Link/Link';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector';
|
||||
import styles from './ImportListExclusion.css';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
|
||||
class ImportListExclusion extends Component {
|
||||
|
||||
@@ -55,28 +58,82 @@ class ImportListExclusion extends Component {
|
||||
render() {
|
||||
const {
|
||||
id,
|
||||
isSelected,
|
||||
onSelectedChange,
|
||||
columns,
|
||||
movieTitle,
|
||||
tmdbId,
|
||||
movieYear
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
styles.importExclusion
|
||||
)}
|
||||
>
|
||||
<div className={styles.tmdbId}>{tmdbId}</div>
|
||||
<div className={styles.movieTitle}>{movieTitle}</div>
|
||||
<div className={styles.movieYear}>{movieYear}</div>
|
||||
<TableRow>
|
||||
<TableSelectCell
|
||||
id={id}
|
||||
isSelected={isSelected}
|
||||
onSelectedChange={onSelectedChange}
|
||||
/>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<Link
|
||||
onPress={this.onEditImportExclusionPress}
|
||||
>
|
||||
<Icon name={icons.EDIT} />
|
||||
</Link>
|
||||
</div>
|
||||
{
|
||||
columns.map((column) => {
|
||||
const {
|
||||
name,
|
||||
isVisible
|
||||
} = column;
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name === 'tmdbId') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{tmdbId}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'movieTitle') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{movieTitle}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'movieYear') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{movieYear}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'actions') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.actions}
|
||||
>
|
||||
<IconButton
|
||||
title={translate('RemoveFromBlocklist')}
|
||||
name={icons.EDIT}
|
||||
onPress={this.onEditImportExclusionPress}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
title={translate('RemoveFromBlocklist')}
|
||||
name={icons.REMOVE}
|
||||
kind={kinds.DANGER}
|
||||
onPress={this.onDeleteImportExclusionPress}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
}
|
||||
|
||||
<EditImportListExclusionModalConnector
|
||||
id={id}
|
||||
@@ -94,7 +151,7 @@ class ImportListExclusion extends Component {
|
||||
onConfirm={this.onConfirmDeleteImportExclusion}
|
||||
onCancel={this.onDeleteImportExclusionModalClose}
|
||||
/>
|
||||
</div>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -104,6 +161,9 @@ ImportListExclusion.propTypes = {
|
||||
movieTitle: PropTypes.string.isRequired,
|
||||
tmdbId: PropTypes.number.isRequired,
|
||||
movieYear: PropTypes.number.isRequired,
|
||||
isSelected: PropTypes.bool.isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onSelectedChange: PropTypes.func.isRequired,
|
||||
onConfirmDeleteImportExclusion: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -4,8 +4,12 @@ import FieldSet from 'Components/FieldSet';
|
||||
import Icon from 'Components/Icon';
|
||||
import Link from 'Components/Link/Link';
|
||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import selectAll from 'Utilities/Table/selectAll';
|
||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||
import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector';
|
||||
import ImportListExclusion from './ImportListExclusion';
|
||||
import styles from './ImportListExclusions.css';
|
||||
@@ -19,6 +23,10 @@ class ImportListExclusions extends Component {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
allSelected: false,
|
||||
allUnselected: false,
|
||||
lastToggled: null,
|
||||
selectedState: {},
|
||||
isAddImportExclusionModalOpen: false
|
||||
};
|
||||
}
|
||||
@@ -26,6 +34,16 @@ class ImportListExclusions extends Component {
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onSelectAllChange = ({ value }) => {
|
||||
this.setState(selectAll(this.state.selectedState, value));
|
||||
};
|
||||
|
||||
onSelectedChange = ({ id, value, shiftKey = false }) => {
|
||||
this.setState((state) => {
|
||||
return toggleSelected(state, this.props.items, id, value, shiftKey);
|
||||
});
|
||||
};
|
||||
|
||||
onAddImportExclusionPress = () => {
|
||||
this.setState({ isAddImportExclusionModalOpen: true });
|
||||
};
|
||||
@@ -41,41 +59,50 @@ class ImportListExclusions extends Component {
|
||||
const {
|
||||
items,
|
||||
onConfirmDeleteImportExclusion,
|
||||
columns,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
allSelected,
|
||||
allUnselected,
|
||||
selectedState
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<FieldSet legend={translate('ListExclusions')}>
|
||||
<PageSectionContent
|
||||
errorMessage={translate('UnableToLoadListExclusions')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.importListExclusionsHeader}>
|
||||
<div className={styles.tmdbId}>
|
||||
TMDb Id
|
||||
</div>
|
||||
<div className={styles.title}>
|
||||
{translate('Title')}
|
||||
</div>
|
||||
<div className={styles.movieYear}>
|
||||
{translate('Year')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{
|
||||
items.map((item, index) => {
|
||||
return (
|
||||
<ImportListExclusion
|
||||
key={item.id}
|
||||
{...item}
|
||||
{...otherProps}
|
||||
index={index}
|
||||
onConfirmDeleteImportExclusion={onConfirmDeleteImportExclusion}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
<Table
|
||||
selectAll={true}
|
||||
allSelected={allSelected}
|
||||
allUnselected={allUnselected}
|
||||
columns={columns}
|
||||
{...otherProps}
|
||||
onSelectAllChange={this.onSelectAllChange}
|
||||
>
|
||||
<TableBody>
|
||||
{
|
||||
items.map((item, index) => {
|
||||
return (
|
||||
<ImportListExclusion
|
||||
key={item.id}
|
||||
isSelected={selectedState[item.id] || false}
|
||||
{...item}
|
||||
{...otherProps}
|
||||
columns={columns}
|
||||
index={index}
|
||||
onSelectedChange={this.onSelectedChange}
|
||||
onConfirmDeleteImportExclusion={onConfirmDeleteImportExclusion}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<div className={styles.addImportExclusion}>
|
||||
@@ -101,6 +128,7 @@ class ImportListExclusions extends Component {
|
||||
ImportListExclusions.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onConfirmDeleteImportExclusion: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHand
|
||||
import createSaveProviderHandler from 'Store/Actions/Creators/createSaveProviderHandler';
|
||||
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
||||
import { createThunk } from 'Store/thunks';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
//
|
||||
// Variables
|
||||
@@ -48,7 +49,34 @@ export default {
|
||||
items: [],
|
||||
isSaving: false,
|
||||
saveError: null,
|
||||
pendingChanges: {}
|
||||
pendingChanges: {},
|
||||
|
||||
columns: [
|
||||
{
|
||||
name: 'tmdbId',
|
||||
label: 'TmdbId',
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'movieTitle',
|
||||
label: translate('Title'),
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'movieYear',
|
||||
label: translate('Year'),
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'actions',
|
||||
columnLabel: translate('Actions'),
|
||||
isVisible: true,
|
||||
isModifiable: false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
//
|
||||
|
||||
@@ -162,6 +162,14 @@ export const filterPredicates = {
|
||||
return predicate(rating, filterValue);
|
||||
},
|
||||
|
||||
rottenTomatoesRating: function(item, filterValue, type) {
|
||||
const predicate = filterTypePredicates[type];
|
||||
|
||||
const rating = item.ratings.rottenTomatoes ? item.ratings.rottenTomatoes.value : 0;
|
||||
|
||||
return predicate(rating, filterValue);
|
||||
},
|
||||
|
||||
imdbVotes: function(item, filterValue, type) {
|
||||
const predicate = filterTypePredicates[type];
|
||||
|
||||
|
||||
@@ -254,27 +254,32 @@ export const actionHandlers = handleThunks({
|
||||
const {
|
||||
collectionIds,
|
||||
monitored,
|
||||
monitor
|
||||
monitor,
|
||||
qualityProfileId,
|
||||
rootFolderPath,
|
||||
minimumAvailability
|
||||
} = payload;
|
||||
|
||||
const response = {};
|
||||
const collections = [];
|
||||
|
||||
collectionIds.forEach((id) => {
|
||||
const collectionToUpdate = { id };
|
||||
|
||||
if (payload.hasOwnProperty('monitored')) {
|
||||
collectionToUpdate.monitored = monitored;
|
||||
}
|
||||
|
||||
collections.push(collectionToUpdate);
|
||||
});
|
||||
if (payload.hasOwnProperty('monitored')) {
|
||||
response.monitored = monitored;
|
||||
}
|
||||
|
||||
if (payload.hasOwnProperty('monitor')) {
|
||||
response.monitorMovies = monitor === 'monitored';
|
||||
}
|
||||
|
||||
response.collections = collections;
|
||||
if (payload.hasOwnProperty('qualityProfileId')) {
|
||||
response.qualityProfileId = qualityProfileId;
|
||||
}
|
||||
|
||||
if (payload.hasOwnProperty('minimumAvailability')) {
|
||||
response.minimumAvailability = minimumAvailability;
|
||||
}
|
||||
|
||||
response.rootFolderPath = rootFolderPath;
|
||||
response.collectionIds = collectionIds;
|
||||
|
||||
dispatch(set({
|
||||
section,
|
||||
|
||||
@@ -178,8 +178,20 @@ export const defaultState = {
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'ratings',
|
||||
label: translate('Ratings'),
|
||||
name: 'tmdbRating',
|
||||
label: translate('TmdbRating'),
|
||||
isSortable: true,
|
||||
isVisible: false
|
||||
},
|
||||
{
|
||||
name: 'rottenTomatoesRating',
|
||||
label: translate('RottenTomatoesRating'),
|
||||
isSortable: true,
|
||||
isVisible: false
|
||||
},
|
||||
{
|
||||
name: 'imdbRating',
|
||||
label: translate('ImdbRating'),
|
||||
isSortable: true,
|
||||
isVisible: false
|
||||
},
|
||||
@@ -224,10 +236,22 @@ export const defaultState = {
|
||||
return originalLanguage.name;
|
||||
},
|
||||
|
||||
ratings: function(item) {
|
||||
imdbRating: function(item) {
|
||||
const { ratings = {} } = item;
|
||||
|
||||
return ratings.tmdb? ratings.tmdb.value : 0;
|
||||
return ratings.imdb ? ratings.imdb.value : 0;
|
||||
},
|
||||
|
||||
tmdbRating: function(item) {
|
||||
const { ratings = {} } = item;
|
||||
|
||||
return ratings.tmdb ? ratings.tmdb.value : 0;
|
||||
},
|
||||
|
||||
rottenTomatoesRating: function(item) {
|
||||
const { ratings = {} } = item;
|
||||
|
||||
return ratings.rottenTomatoes ? ratings.rottenTomatoes.value : 0;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -413,6 +437,11 @@ export const defaultState = {
|
||||
label: translate('ImdbRating'),
|
||||
type: filterBuilderTypes.NUMBER
|
||||
},
|
||||
{
|
||||
name: 'rottenTomatoesRating',
|
||||
label: translate('RottenTomatoesRating'),
|
||||
type: filterBuilderTypes.NUMBER
|
||||
},
|
||||
{
|
||||
name: 'imdbVotes',
|
||||
label: translate('ImdbVotes'),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -13,7 +13,7 @@ namespace NzbDrone.Common.Test
|
||||
{
|
||||
[TestFixture]
|
||||
|
||||
public class ConfigFileWriterTest : TestBase<ConfigFileWriter>
|
||||
public class ConfigFileProviderTest : TestBase<ConfigFileProvider>
|
||||
{
|
||||
private string _configFileContents;
|
||||
private string _configFilePath;
|
||||
@@ -45,7 +45,56 @@ namespace NzbDrone.Common.Test
|
||||
.Callback<string, string>((p, t) => _configFileContents = t);
|
||||
}
|
||||
|
||||
/*
|
||||
[Test]
|
||||
public void GetValue_Success()
|
||||
{
|
||||
const string key = "Port";
|
||||
const string value = "7878";
|
||||
|
||||
var result = Subject.GetValue(key, value);
|
||||
|
||||
result.Should().Be(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetInt_Success()
|
||||
{
|
||||
const string key = "Port";
|
||||
const int value = 7878;
|
||||
|
||||
var result = Subject.GetValueInt(key, value);
|
||||
|
||||
result.Should().Be(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetBool_Success()
|
||||
{
|
||||
const string key = "LaunchBrowser";
|
||||
const bool value = true;
|
||||
|
||||
var result = Subject.GetValueBoolean(key, value);
|
||||
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetLaunchBrowser_Success()
|
||||
{
|
||||
var result = Subject.LaunchBrowser;
|
||||
|
||||
result.Should().Be(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetPort_Success()
|
||||
{
|
||||
const int value = 7878;
|
||||
|
||||
var result = Subject.Port;
|
||||
|
||||
result.Should().Be(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetValue_bool()
|
||||
@@ -71,6 +120,17 @@ namespace NzbDrone.Common.Test
|
||||
result.Should().Be(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetValue_New_Key()
|
||||
{
|
||||
const string key = "Hello";
|
||||
const string value = "World";
|
||||
|
||||
var result = Subject.GetValue(key, value);
|
||||
|
||||
result.Should().Be(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAuthenticationType_No_Existing_Value()
|
||||
{
|
||||
@@ -79,7 +139,6 @@ namespace NzbDrone.Common.Test
|
||||
result.Should().Be(AuthenticationType.None);
|
||||
}
|
||||
|
||||
/*
|
||||
[Test]
|
||||
public void SaveDictionary_should_save_proper_value()
|
||||
{
|
||||
@@ -111,6 +170,32 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
Subject.Port.Should().Be(port);
|
||||
Subject.SslPort.Should().Be(sslPort);
|
||||
}*/
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_config_file_is_empty()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.FileExists(_configFilePath))
|
||||
.Returns(true);
|
||||
|
||||
Assert.Throws<InvalidConfigFileException>(() => Subject.GetValue("key", "value"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_config_file_contains_only_null_character()
|
||||
{
|
||||
_configFileContents = "\0";
|
||||
|
||||
Assert.Throws<InvalidConfigFileException>(() => Subject.GetValue("key", "value"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_config_file_contains_invalid_xml()
|
||||
{
|
||||
_configFileContents = "{ \"key\": \"value\" }";
|
||||
|
||||
Assert.Throws<InvalidConfigFileException>(() => Subject.GetValue("key", "value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using System.Linq;
|
||||
using DryIoc;
|
||||
using DryIoc.Microsoft.DependencyInjection;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -11,7 +10,7 @@ using NUnit.Framework;
|
||||
using NzbDrone.Common.Composition.Extensions;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -33,8 +32,7 @@ namespace NzbDrone.Common.Test
|
||||
.AddStartupContext(new StartupContext("first", "second"));
|
||||
|
||||
container.RegisterInstance(new Mock<IHostLifetime>().Object);
|
||||
container.RegisterInstance(new Mock<IConfiguration>().Object);
|
||||
container.RegisterInstance(new Mock<IOptionsMonitor<ConfigFileOptions>>().Object);
|
||||
container.RegisterInstance(new Mock<IOptions<PostgresOptions>>().Object);
|
||||
|
||||
var serviceProvider = container.GetServiceProvider();
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NLog.Fluent;
|
||||
|
||||
@@ -8,47 +10,46 @@ namespace NzbDrone.Common.Instrumentation.Extensions
|
||||
{
|
||||
public static readonly Logger SentryLogger = LogManager.GetLogger("Sentry");
|
||||
|
||||
public static LogBuilder SentryFingerprint(this LogBuilder logBuilder, params string[] fingerprint)
|
||||
public static LogEventBuilder SentryFingerprint(this LogEventBuilder logBuilder, params string[] fingerprint)
|
||||
{
|
||||
return logBuilder.Property("Sentry", fingerprint);
|
||||
}
|
||||
|
||||
public static LogBuilder WriteSentryDebug(this LogBuilder logBuilder, params string[] fingerprint)
|
||||
public static LogEventBuilder WriteSentryDebug(this LogEventBuilder logBuilder, params string[] fingerprint)
|
||||
{
|
||||
return LogSentryMessage(logBuilder, LogLevel.Debug, fingerprint);
|
||||
}
|
||||
|
||||
public static LogBuilder WriteSentryInfo(this LogBuilder logBuilder, params string[] fingerprint)
|
||||
public static LogEventBuilder WriteSentryInfo(this LogEventBuilder logBuilder, params string[] fingerprint)
|
||||
{
|
||||
return LogSentryMessage(logBuilder, LogLevel.Info, fingerprint);
|
||||
}
|
||||
|
||||
public static LogBuilder WriteSentryWarn(this LogBuilder logBuilder, params string[] fingerprint)
|
||||
public static LogEventBuilder WriteSentryWarn(this LogEventBuilder logBuilder, params string[] fingerprint)
|
||||
{
|
||||
return LogSentryMessage(logBuilder, LogLevel.Warn, fingerprint);
|
||||
}
|
||||
|
||||
public static LogBuilder WriteSentryError(this LogBuilder logBuilder, params string[] fingerprint)
|
||||
public static LogEventBuilder WriteSentryError(this LogEventBuilder logBuilder, params string[] fingerprint)
|
||||
{
|
||||
return LogSentryMessage(logBuilder, LogLevel.Error, fingerprint);
|
||||
}
|
||||
|
||||
private static LogBuilder LogSentryMessage(LogBuilder logBuilder, LogLevel level, string[] fingerprint)
|
||||
private static LogEventBuilder LogSentryMessage(LogEventBuilder logBuilder, LogLevel level, string[] fingerprint)
|
||||
{
|
||||
SentryLogger.Log(level)
|
||||
.CopyLogEvent(logBuilder.LogEventInfo)
|
||||
SentryLogger.ForLogEvent(level)
|
||||
.CopyLogEvent(logBuilder.LogEvent)
|
||||
.SentryFingerprint(fingerprint)
|
||||
.Write();
|
||||
.Log();
|
||||
|
||||
return logBuilder.Property("Sentry", null);
|
||||
return logBuilder.Property<string>("Sentry", null);
|
||||
}
|
||||
|
||||
private static LogBuilder CopyLogEvent(this LogBuilder logBuilder, LogEventInfo logEvent)
|
||||
private static LogEventBuilder CopyLogEvent(this LogEventBuilder logBuilder, LogEventInfo logEvent)
|
||||
{
|
||||
return logBuilder.LoggerName(logEvent.LoggerName)
|
||||
.TimeStamp(logEvent.TimeStamp)
|
||||
return logBuilder.TimeStamp(logEvent.TimeStamp)
|
||||
.Message(logEvent.Message, logEvent.Parameters)
|
||||
.Properties(logEvent.Properties.ToDictionary(v => v.Key, v => v.Value))
|
||||
.Properties(logEvent.Properties.Select(p => new KeyValuePair<string, object>(p.Key.ToString(), p.Value)))
|
||||
.Exception(logEvent.Exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NLog.Targets;
|
||||
|
||||
namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
public class NzbDroneFileTarget : FileTarget
|
||||
{
|
||||
protected override string GetFormattedMessage(LogEventInfo logEvent)
|
||||
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
|
||||
{
|
||||
return CleanseLogMessage.Cleanse(Layout.Render(logEvent));
|
||||
var result = CleanseLogMessage.Cleanse(Layout.Render(logEvent));
|
||||
target.Append(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace NzbDrone.Common.Instrumentation
|
||||
|
||||
var appFolderInfo = new AppFolderInfo(startupContext);
|
||||
|
||||
RegisterGlobalFilters();
|
||||
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
RegisterDebugger();
|
||||
@@ -97,10 +99,21 @@ namespace NzbDrone.Common.Instrumentation
|
||||
target.Layout = "[${level}] [${threadid}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
|
||||
var loggingRule = new LoggingRule("*", LogLevel.Trace, target);
|
||||
|
||||
LogManager.Configuration.AddTarget("debugger", target);
|
||||
LogManager.Configuration.LoggingRules.Add(loggingRule);
|
||||
}
|
||||
|
||||
private static void RegisterGlobalFilters()
|
||||
{
|
||||
LogManager.Setup().LoadConfiguration(c =>
|
||||
{
|
||||
c.ForLogger("Microsoft.Hosting.Lifetime*").WriteToNil(LogLevel.Info);
|
||||
c.ForLogger("System*").WriteToNil(LogLevel.Warn);
|
||||
c.ForLogger("Microsoft*").WriteToNil(LogLevel.Warn);
|
||||
});
|
||||
}
|
||||
|
||||
private static void RegisterConsole()
|
||||
{
|
||||
var level = LogLevel.Trace;
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NLog" Version="4.7.14" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.4" />
|
||||
<PackageReference Include="NLog" Version="5.0.1" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
|
||||
<PackageReference Include="Sentry" Version="3.15.0" />
|
||||
<PackageReference Include="NLog.Targets.Syslog" Version="6.0.3" />
|
||||
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.4" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
|
||||
@@ -56,6 +56,51 @@ namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
movies.First().CollectionTmdbId.Should().Be(collections.First().TmdbId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_collection_from_movie_without_name()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Movies").Row(new
|
||||
{
|
||||
Monitored = true,
|
||||
MinimumAvailability = 4,
|
||||
ProfileId = 1,
|
||||
MovieFileId = 0,
|
||||
MovieMetadataId = 1,
|
||||
Path = string.Format("/Movies/{0}", "Title"),
|
||||
});
|
||||
|
||||
c.Insert.IntoTable("MovieMetadata").Row(new
|
||||
{
|
||||
Title = "Title",
|
||||
CleanTitle = "CleanTitle",
|
||||
Status = 3,
|
||||
Images = new[] { new { CoverType = "Poster" } }.ToJson(),
|
||||
Recommendations = new[] { 1 }.ToJson(),
|
||||
Runtime = 90,
|
||||
OriginalTitle = "Title",
|
||||
CleanOriginalTitle = "CleanTitle",
|
||||
OriginalLanguage = 1,
|
||||
TmdbId = 132456,
|
||||
Collection = new { TmdbId = 11 }.ToJson(),
|
||||
LastInfoSync = DateTime.UtcNow,
|
||||
});
|
||||
});
|
||||
|
||||
var collections = db.Query<Collection208>("SELECT \"Id\", \"Title\", \"TmdbId\", \"Monitored\" FROM \"Collections\"");
|
||||
|
||||
collections.Should().HaveCount(1);
|
||||
collections.First().TmdbId.Should().Be(11);
|
||||
collections.First().Title.Should().Be("Collection 11");
|
||||
collections.First().Monitored.Should().BeFalse();
|
||||
|
||||
var movies = db.Query<Movie208>("SELECT \"Id\", \"CollectionTmdbId\" FROM \"MovieMetadata\"");
|
||||
|
||||
movies.Should().HaveCount(1);
|
||||
movies.First().CollectionTmdbId.Should().Be(collections.First().TmdbId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_duplicate_collection()
|
||||
{
|
||||
|
||||
@@ -3,11 +3,9 @@ using System.Collections.Generic;
|
||||
using System.Data.SQLite;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Npgsql;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -126,13 +124,13 @@ namespace NzbDrone.Core.Test.Framework
|
||||
|
||||
private void CreatePostgresDb()
|
||||
{
|
||||
var options = Mocker.Resolve<IOptionsMonitor<ConfigFileOptions>>().CurrentValue;
|
||||
var options = Mocker.Resolve<IOptions<PostgresOptions>>().Value;
|
||||
PostgresDatabase.Create(options, MigrationType);
|
||||
}
|
||||
|
||||
private void DropPostgresDb()
|
||||
{
|
||||
var options = Mocker.Resolve<IOptionsMonitor<ConfigFileOptions>>().CurrentValue;
|
||||
var options = Mocker.Resolve<IOptions<PostgresOptions>>().Value;
|
||||
PostgresDatabase.Drop(options, MigrationType);
|
||||
}
|
||||
|
||||
@@ -176,11 +174,12 @@ namespace NzbDrone.Core.Test.Framework
|
||||
SetupLogging();
|
||||
|
||||
// populate the possible postgres options
|
||||
var options = PostgresDatabase.GetTestOptions();
|
||||
_databaseType = options.PostgresHost.IsNotNullOrWhiteSpace() ? DatabaseType.PostgreSQL : DatabaseType.SQLite;
|
||||
var postgresOptions = PostgresDatabase.GetTestOptions();
|
||||
_databaseType = postgresOptions.Host.IsNotNullOrWhiteSpace() ? DatabaseType.PostgreSQL : DatabaseType.SQLite;
|
||||
|
||||
// Set up remaining container services
|
||||
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>().Setup(x => x.CurrentValue).Returns(options);
|
||||
Mocker.SetConstant(Options.Create(postgresOptions));
|
||||
Mocker.SetConstant<IConfigFileProvider>(Mocker.Resolve<ConfigFileProvider>());
|
||||
Mocker.SetConstant<IConnectionStringFactory>(Mocker.Resolve<ConnectionStringFactory>());
|
||||
Mocker.SetConstant<IMigrationController>(Mocker.Resolve<MigrationController>());
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class LegacyPostgresCheckFixture : CoreTest<LegacyPostgresCheck>
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Mocker.GetMock<ILocalizationService>()
|
||||
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
|
||||
.Returns("Warning {0} -> {1}");
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Teardown()
|
||||
{
|
||||
foreach (var name in new[] { "__Postgres__Host", "__Postgres__Port", ":Postgres:Host", ":Postgres:Port" })
|
||||
{
|
||||
Environment.SetEnvironmentVariable(BuildInfo.AppName + name, null);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_ok_normally()
|
||||
{
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[TestCase("__")]
|
||||
[TestCase(":")]
|
||||
public void should_return_error_if_vars_defined(string separator)
|
||||
{
|
||||
Environment.SetEnvironmentVariable(BuildInfo.AppName + separator + "Postgres" + separator + "Host", "localhost");
|
||||
Environment.SetEnvironmentVariable(BuildInfo.AppName + separator + "Postgres" + separator + "Port", "localhost");
|
||||
|
||||
var result = Subject.Check();
|
||||
result.ShouldBeError("Warning " + BuildInfo.AppName + separator + "Postgres" + separator + "Host, " +
|
||||
BuildInfo.AppName + separator + "Postgres" + separator + "Port -> " +
|
||||
BuildInfo.AppName + separator + "PostgresHost, " +
|
||||
BuildInfo.AppName + separator + "PostgresPort");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Update;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
@@ -22,9 +20,9 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
|
||||
private void GivenValidBranch(string branch)
|
||||
{
|
||||
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
|
||||
.Setup(s => s.CurrentValue)
|
||||
.Returns(new ConfigFileOptions { Branch = branch });
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.SetupGet(s => s.Branch)
|
||||
.Returns(branch);
|
||||
}
|
||||
|
||||
[TestCase("aphrodite")]
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
public void should_return_ok_on_movie_imported_event()
|
||||
{
|
||||
GivenFolderExists(_downloadRootPath);
|
||||
var importEvent = new MovieImportedEvent(new LocalMovie(), new MovieFile(), new List<MovieFile>(), true, new DownloadClientItem());
|
||||
var importEvent = new MovieFileImportedEvent(new LocalMovie(), new MovieFile(), new List<MovieFile>(), true, new DownloadClientItem());
|
||||
|
||||
Subject.Check(importEvent).ShouldBeOk();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
@@ -20,10 +19,6 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
Mocker.GetMock<ILocalizationService>()
|
||||
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
|
||||
.Returns("Some Warning Message");
|
||||
|
||||
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
|
||||
.Setup(c => c.CurrentValue)
|
||||
.Returns(new ConfigFileOptions());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -49,9 +44,9 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
|
||||
const string startupFolder = @"/opt/nzbdrone";
|
||||
|
||||
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
|
||||
.Setup(s => s.CurrentValue)
|
||||
.Returns(new ConfigFileOptions { UpdateAutomatically = true });
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.Setup(s => s.UpdateAutomatically)
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IAppFolderInfo>()
|
||||
.Setup(s => s.StartUpFolder)
|
||||
@@ -72,9 +67,9 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
const string startupFolder = @"/opt/nzbdrone";
|
||||
const string uiFolder = @"/opt/nzbdrone/UI";
|
||||
|
||||
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
|
||||
.Setup(s => s.CurrentValue)
|
||||
.Returns(new ConfigFileOptions { UpdateAutomatically = true });
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.Setup(s => s.UpdateAutomatically)
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IAppFolderInfo>()
|
||||
.Setup(s => s.StartUpFolder)
|
||||
@@ -96,9 +91,13 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
PosixOnly();
|
||||
|
||||
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
|
||||
.Setup(s => s.CurrentValue)
|
||||
.Returns(new ConfigFileOptions { UpdateAutomatically = true, UpdateMechanism = UpdateMechanism.Script });
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.Setup(s => s.UpdateAutomatically)
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.Setup(s => s.UpdateMechanism)
|
||||
.Returns(UpdateMechanism.Script);
|
||||
|
||||
Mocker.GetMock<IAppFolderInfo>()
|
||||
.Setup(s => s.StartUpFolder)
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace NzbDrone.Core.Test.HistoryTests
|
||||
DownloadId = "abcd"
|
||||
};
|
||||
|
||||
Subject.Handle(new MovieImportedEvent(localMovie, movieFile, new List<MovieFile>(), true, downloadClientItem));
|
||||
Subject.Handle(new MovieFileImportedEvent(localMovie, movieFile, new List<MovieFile>(), true, downloadClientItem));
|
||||
|
||||
Mocker.GetMock<IHistoryRepository>()
|
||||
.Verify(v => v.Insert(It.Is<MovieHistory>(h => h.SourceTitle == Path.GetFileNameWithoutExtension(localMovie.Path))));
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_unorphaned_collection_items()
|
||||
public void should_delete_orphaned_collection_with_meta_but_no_movie_items()
|
||||
{
|
||||
var collection = Builder<MovieCollection>.CreateNew()
|
||||
.With(h => h.Id = 3)
|
||||
@@ -40,6 +40,27 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
|
||||
Db.Insert(movie);
|
||||
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().HaveCount(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_unorphaned_collection()
|
||||
{
|
||||
var collection = Builder<MovieCollection>.CreateNew()
|
||||
.With(h => h.Id = 3)
|
||||
.With(h => h.TmdbId = 123456)
|
||||
.With(h => h.Title = "Some Credit")
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(collection);
|
||||
|
||||
var movieMeta = Builder<MovieMetadata>.CreateNew().With(m => m.CollectionTmdbId = collection.TmdbId).BuildNew();
|
||||
Db.Insert(movieMeta);
|
||||
|
||||
var movie = Builder<Movie>.CreateNew().With(m => m.MovieMetadataId = movieMeta.Id).BuildNew();
|
||||
Db.Insert(movie);
|
||||
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().HaveCount(1);
|
||||
}
|
||||
|
||||
@@ -4,12 +4,10 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Movies.Events;
|
||||
@@ -33,10 +31,6 @@ namespace NzbDrone.Core.Test.MediaCoverTests
|
||||
.Build();
|
||||
|
||||
Mocker.GetMock<IMovieService>().Setup(m => m.GetMovie(It.Is<int>(id => id == _movie.Id))).Returns(_movie);
|
||||
|
||||
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
|
||||
.Setup(x => x.CurrentValue)
|
||||
.Returns(new ConfigFileOptions());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport
|
||||
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true);
|
||||
|
||||
Mocker.GetMock<IEventAggregator>()
|
||||
.Verify(v => v.PublishEvent(It.IsAny<MovieImportedEvent>()), Times.Once());
|
||||
.Verify(v => v.PublishEvent(It.IsAny<MovieFileImportedEvent>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -67,6 +67,35 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
Parser.Parser.ParseMovieTitle(postTitle).PrimaryMovieTitle.Should().Be(title);
|
||||
}
|
||||
|
||||
[TestCase("[MTBB] Kimi no Na wa. (2016) v2 [97681524].mkv", "Kimi no Na wa", "MTBB", 2016)]
|
||||
[TestCase("[sam] Toward the Terra (1980) [BD 1080p TrueHD].mkv", "Toward the Terra", "sam", 1980)]
|
||||
public void should_parse_anime_movie_title(string postTitle, string title, string releaseGroup, int year)
|
||||
{
|
||||
ParsedMovieInfo movie = Parser.Parser.ParseMovieTitle(postTitle);
|
||||
using (new AssertionScope())
|
||||
{
|
||||
movie.PrimaryMovieTitle.Should().Be(title);
|
||||
movie.ReleaseGroup.Should().Be(releaseGroup);
|
||||
movie.Year.Should().Be(year);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("[Arid] Cowboy Bebop - Knockin' on Heaven's Door v2 [00F4CDA0].mkv", "Cowboy Bebop - Knockin' on Heaven's Door", "Arid")]
|
||||
[TestCase("[Baws] Evangelion 1.11 - You Are (Not) Alone v2 (1080p BD HEVC FLAC) [BF42B1C8].mkv", "Evangelion 1 11 - You Are (Not) Alone", "Baws")]
|
||||
[TestCase("[Arid] 5 Centimeters per Second (BDRip 1920x1080 Hi10 FLAC) [FD8B6FF2].mkv", "5 Centimeters per Second", "Arid")]
|
||||
[TestCase("[Baws] Evangelion 2.22 - You Can (Not) Advance (1080p BD HEVC FLAC) [56E7A5B8].mkv", "Evangelion 2 22 - You Can (Not) Advance", "Baws")]
|
||||
[TestCase("[sam] Goblin Slayer - Goblin's Crown [BD 1080p FLAC] [CD298D48].mkv", "Goblin Slayer - Goblin's Crown", "sam")]
|
||||
[TestCase("[Kulot] Violet Evergarden Gaiden Eien to Jidou Shuki Ningyou [Dual-Audio][BDRip 1920x804 HEVC FLACx2] [91FC62A8].mkv", "Violet Evergarden Gaiden Eien to Jidou Shuki Ningyou", "Kulot")]
|
||||
public void should_parse_anime_movie_title_without_year(string postTitle, string title, string releaseGroup)
|
||||
{
|
||||
ParsedMovieInfo movie = Parser.Parser.ParseMovieTitle(postTitle);
|
||||
using (new AssertionScope())
|
||||
{
|
||||
movie.PrimaryMovieTitle.Should().Be(title);
|
||||
movie.ReleaseGroup.Should().Be(releaseGroup);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("Movie.Aufbruch.nach.Pandora.Extended.2009.German.DTS.720p.BluRay.x264-SoW", "Movie Aufbruch nach Pandora", "Extended", 2009)]
|
||||
[TestCase("Drop.Movie.1994.German.AC3D.DL.720p.BluRay.x264-KLASSiGERHD", "Drop Movie", "", 1994)]
|
||||
[TestCase("Kick.Movie.2.2013.German.DTS.DL.720p.BluRay.x264-Pate", "Kick Movie 2", "", 2013)]
|
||||
|
||||
@@ -189,6 +189,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie.Title.2020.MULTi.1080p.WEB.H264-ALLDAYiN (S:285/L:11)", false)]
|
||||
[TestCase("Movie Title (2020) MULTi WEB 1080p x264-JiHEFF (S:317/L:28)", false)]
|
||||
[TestCase("Movie.Titles.2020.1080p.NF.WEB.DD2.0.x264-SNEAkY", false)]
|
||||
[TestCase("The.Movie.2022.NORDiC.1080p.DV.HDR.WEB.H 265-NiDHUG", false)]
|
||||
public void should_parse_webdl1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R1080p);
|
||||
@@ -207,6 +208,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Movie.Name.2016.03.14.2160p.WEB.PROPER.h264-spamTV", true)]
|
||||
[TestCase("[HorribleSubs] Movie Title! 2018 [Web][MKV][h264][2160p][AAC 2.0][Softsubs (HorribleSubs)]", false)]
|
||||
[TestCase("Movie Name 2020 WEB-DL 4K H265 10bit HDR DDP5.1 Atmos-PTerWEB", false)]
|
||||
[TestCase("The.Movie.2022.NORDiC.2160p.DV.HDR.WEB.H.265-NiDHUG", false)]
|
||||
public void should_parse_webdl2160p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R2160p);
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common;
|
||||
@@ -61,9 +60,9 @@ namespace NzbDrone.Core.Test.UpdateTests
|
||||
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetCurrentProcess()).Returns(new ProcessInfo { Id = 12 });
|
||||
Mocker.GetMock<IRuntimeInfo>().Setup(c => c.ExecutingApplication).Returns(@"C:\Test\Radarr.exe");
|
||||
|
||||
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
|
||||
.Setup(s => s.CurrentValue)
|
||||
.Returns(new ConfigFileOptions { UpdateAutomatically = true });
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.SetupGet(s => s.UpdateAutomatically)
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.FolderWritable(It.IsAny<string>()))
|
||||
@@ -78,9 +77,13 @@ namespace NzbDrone.Core.Test.UpdateTests
|
||||
|
||||
private void GivenInstallScript(string path)
|
||||
{
|
||||
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
|
||||
.Setup(s => s.CurrentValue)
|
||||
.Returns(new ConfigFileOptions { UpdateAutomatically = true, UpdateMechanism = UpdateMechanism.Script, UpdateScriptPath = path });
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.SetupGet(s => s.UpdateMechanism)
|
||||
.Returns(UpdateMechanism.Script);
|
||||
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.SetupGet(s => s.UpdateScriptPath)
|
||||
.Returns(path);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.FileExists(path, StringComparison.Ordinal))
|
||||
@@ -331,7 +334,7 @@ namespace NzbDrone.Core.Test.UpdateTests
|
||||
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
Mocker.GetMock<IConfigFileWriter>()
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.Verify(v => v.SaveConfigDictionary(It.Is<Dictionary<string, object>>(d => d.ContainsKey("Branch") && (string)d["Branch"] == "fake")), Times.Once());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Datastore;
|
||||
@@ -16,16 +15,16 @@ namespace NzbDrone.Core.Analytics
|
||||
|
||||
public class AnalyticsService : IAnalyticsService
|
||||
{
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IHistoryService _historyService;
|
||||
|
||||
public AnalyticsService(IHistoryService historyService, IOptionsMonitor<ConfigFileOptions> configFileOptions)
|
||||
public AnalyticsService(IHistoryService historyService, IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_configFileOptions = configFileOptions;
|
||||
_configFileProvider = configFileProvider;
|
||||
_historyService = historyService;
|
||||
}
|
||||
|
||||
public bool IsEnabled => (_configFileOptions.CurrentValue.AnalyticsEnabled && RuntimeInfo.IsProduction) || RuntimeInfo.IsDevelopment;
|
||||
public bool IsEnabled => (_configFileProvider.AnalyticsEnabled && RuntimeInfo.IsProduction) || RuntimeInfo.IsDevelopment;
|
||||
|
||||
public bool InstallIsActive
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -20,22 +19,22 @@ namespace NzbDrone.Core.Authentication
|
||||
User FindUser(Guid identifier);
|
||||
}
|
||||
|
||||
public class UserService : IUserService
|
||||
public class UserService : IUserService, IHandle<ApplicationStartedEvent>
|
||||
{
|
||||
private readonly IUserRepository _repo;
|
||||
private readonly IAppFolderInfo _appFolderInfo;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
|
||||
public UserService(IUserRepository repo,
|
||||
IAppFolderInfo appFolderInfo,
|
||||
IDiskProvider diskProvider,
|
||||
IOptionsMonitor<ConfigFileOptions> configFileOptions)
|
||||
IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_repo = repo;
|
||||
_appFolderInfo = appFolderInfo;
|
||||
_diskProvider = diskProvider;
|
||||
_configFileOptions = configFileOptions;
|
||||
_configFileProvider = configFileProvider;
|
||||
}
|
||||
|
||||
public User Add(string username, string password)
|
||||
@@ -103,5 +102,28 @@ namespace NzbDrone.Core.Authentication
|
||||
{
|
||||
return _repo.FindUser(identifier);
|
||||
}
|
||||
|
||||
public void Handle(ApplicationStartedEvent message)
|
||||
{
|
||||
if (_repo.All().Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var xDoc = _configFileProvider.LoadConfigFile();
|
||||
var config = xDoc.Descendants("Config").Single();
|
||||
var usernameElement = config.Descendants("Username").FirstOrDefault();
|
||||
var passwordElement = config.Descendants("Password").FirstOrDefault();
|
||||
|
||||
if (usernameElement == null || passwordElement == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var username = usernameElement.Value;
|
||||
var password = passwordElement.Value;
|
||||
|
||||
Add(username, password);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Update;
|
||||
|
||||
namespace NzbDrone.Core.Configuration
|
||||
{
|
||||
public class ConfigFileOptions
|
||||
{
|
||||
[Persist]
|
||||
public string BindAddress { get; set; } = "*";
|
||||
[Persist]
|
||||
public int Port { get; set; } = 7878;
|
||||
[Persist]
|
||||
public int SslPort { get; set; } = 9898;
|
||||
[Persist]
|
||||
public bool EnableSsl { get; set; }
|
||||
[Persist]
|
||||
public bool LaunchBrowser { get; set; } = true;
|
||||
public AuthenticationType AuthenticationMethod { get; set; }
|
||||
public bool AnalyticsEnabled { get; set; } = true;
|
||||
[Persist]
|
||||
public string Branch { get; set; } = "master";
|
||||
[Persist]
|
||||
public string LogLevel { get; set; } = "info";
|
||||
public string ConsoleLogLevel { get; set; } = string.Empty;
|
||||
public bool LogSql { get; set; }
|
||||
public int LogRotate { get; set; } = 50;
|
||||
public bool FilterSentryEvents { get; set; } = true;
|
||||
[Persist]
|
||||
public string ApiKey { get; set; } = GenerateApiKey();
|
||||
[Persist]
|
||||
public string SslCertPath { get; set; }
|
||||
[Persist]
|
||||
public string SslCertPassword { get; set; }
|
||||
[Persist]
|
||||
public string UrlBase { get; set; } = string.Empty;
|
||||
[Persist]
|
||||
public string InstanceName { get; set; } = BuildInfo.AppName;
|
||||
public bool UpdateAutomatically { get; set; }
|
||||
public UpdateMechanism UpdateMechanism { get; set; } = UpdateMechanism.BuiltIn;
|
||||
public string UpdateScriptPath { get; set; } = string.Empty;
|
||||
public string SyslogServer { get; set; } = string.Empty;
|
||||
public int SyslogPort { get; set; } = 514;
|
||||
public string SyslogLevel { get; set; } = "info";
|
||||
public string PostgresHost { get; set; }
|
||||
public int PostgresPort { get; set; }
|
||||
public string PostgresUser { get; set; }
|
||||
public string PostgresPassword { get; set; }
|
||||
public string PostgresMainDb { get; set; } = BuildInfo.AppName.ToLower() + "-main";
|
||||
public string PostgresLogDb { get; set; } = BuildInfo.AppName.ToLower() + "-log";
|
||||
|
||||
private static string GenerateApiKey()
|
||||
{
|
||||
return Guid.NewGuid().ToString().Replace("-", "");
|
||||
}
|
||||
|
||||
public static ConfigFileOptions GetOptions()
|
||||
{
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables($"{BuildInfo.AppName}:")
|
||||
.Build();
|
||||
|
||||
var options = new ConfigFileOptions();
|
||||
config.Bind(options);
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
||||
401
src/NzbDrone.Core/Configuration/ConfigFileProvider.cs
Normal file
401
src/NzbDrone.Core/Configuration/ConfigFileProvider.cs
Normal file
@@ -0,0 +1,401 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration.Events;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Update;
|
||||
|
||||
namespace NzbDrone.Core.Configuration
|
||||
{
|
||||
public interface IConfigFileProvider : IHandleAsync<ApplicationStartedEvent>,
|
||||
IExecute<ResetApiKeyCommand>
|
||||
{
|
||||
XDocument LoadConfigFile();
|
||||
Dictionary<string, object> GetConfigDictionary();
|
||||
void SaveConfigDictionary(Dictionary<string, object> configValues);
|
||||
|
||||
string BindAddress { get; }
|
||||
int Port { get; }
|
||||
int SslPort { get; }
|
||||
bool EnableSsl { get; }
|
||||
bool LaunchBrowser { get; }
|
||||
AuthenticationType AuthenticationMethod { get; }
|
||||
bool AnalyticsEnabled { get; }
|
||||
string LogLevel { get; }
|
||||
string ConsoleLogLevel { get; }
|
||||
bool LogSql { get; }
|
||||
int LogRotate { get; }
|
||||
bool FilterSentryEvents { get; }
|
||||
string Branch { get; }
|
||||
string ApiKey { get; }
|
||||
string SslCertPath { get; }
|
||||
string SslCertPassword { get; }
|
||||
string UrlBase { get; }
|
||||
string UiFolder { get; }
|
||||
string InstanceName { get; }
|
||||
bool UpdateAutomatically { get; }
|
||||
UpdateMechanism UpdateMechanism { get; }
|
||||
string UpdateScriptPath { get; }
|
||||
string SyslogServer { get; }
|
||||
int SyslogPort { get; }
|
||||
string SyslogLevel { get; }
|
||||
string PostgresHost { get; }
|
||||
int PostgresPort { get; }
|
||||
string PostgresUser { get; }
|
||||
string PostgresPassword { get; }
|
||||
string PostgresMainDb { get; }
|
||||
string PostgresLogDb { get; }
|
||||
}
|
||||
|
||||
public class ConfigFileProvider : IConfigFileProvider
|
||||
{
|
||||
public const string CONFIG_ELEMENT_NAME = "Config";
|
||||
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly ICached<string> _cache;
|
||||
private readonly PostgresOptions _postgresOptions;
|
||||
|
||||
private readonly string _configFile;
|
||||
private static readonly Regex HiddenCharacterRegex = new Regex("[^a-z0-9]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly object Mutex = new object();
|
||||
|
||||
public ConfigFileProvider(IAppFolderInfo appFolderInfo,
|
||||
ICacheManager cacheManager,
|
||||
IEventAggregator eventAggregator,
|
||||
IDiskProvider diskProvider,
|
||||
IOptions<PostgresOptions> postgresOptions)
|
||||
{
|
||||
_cache = cacheManager.GetCache<string>(GetType());
|
||||
_eventAggregator = eventAggregator;
|
||||
_diskProvider = diskProvider;
|
||||
_configFile = appFolderInfo.GetConfigPath();
|
||||
_postgresOptions = postgresOptions.Value;
|
||||
}
|
||||
|
||||
public Dictionary<string, object> GetConfigDictionary()
|
||||
{
|
||||
var dict = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
var type = GetType();
|
||||
var properties = type.GetProperties();
|
||||
|
||||
foreach (var propertyInfo in properties)
|
||||
{
|
||||
var value = propertyInfo.GetValue(this, null);
|
||||
|
||||
dict.Add(propertyInfo.Name, value);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public void SaveConfigDictionary(Dictionary<string, object> configValues)
|
||||
{
|
||||
_cache.Clear();
|
||||
|
||||
var allWithDefaults = GetConfigDictionary();
|
||||
|
||||
foreach (var configValue in configValues)
|
||||
{
|
||||
if (configValue.Key.Equals("ApiKey", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
object currentValue;
|
||||
allWithDefaults.TryGetValue(configValue.Key, out currentValue);
|
||||
if (currentValue == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var equal = configValue.Value.ToString().Equals(currentValue.ToString());
|
||||
|
||||
if (!equal)
|
||||
{
|
||||
SetValue(configValue.Key.FirstCharToUpper(), configValue.Value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(new ConfigFileSavedEvent());
|
||||
}
|
||||
|
||||
public string BindAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
const string defaultValue = "*";
|
||||
|
||||
string bindAddress = GetValue("BindAddress", defaultValue);
|
||||
if (string.IsNullOrWhiteSpace(bindAddress))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return bindAddress;
|
||||
}
|
||||
}
|
||||
|
||||
public int Port => GetValueInt("Port", 7878);
|
||||
|
||||
public int SslPort => GetValueInt("SslPort", 9898);
|
||||
|
||||
public bool EnableSsl => GetValueBoolean("EnableSsl", false);
|
||||
|
||||
public bool LaunchBrowser => GetValueBoolean("LaunchBrowser", true);
|
||||
|
||||
public string ApiKey
|
||||
{
|
||||
get
|
||||
{
|
||||
var apiKey = GetValue("ApiKey", GenerateApiKey());
|
||||
|
||||
if (apiKey.IsNullOrWhiteSpace())
|
||||
{
|
||||
apiKey = GenerateApiKey();
|
||||
SetValue("ApiKey", apiKey);
|
||||
}
|
||||
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticationType AuthenticationMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
var enabled = GetValueBoolean("AuthenticationEnabled", false, false);
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
SetValue("AuthenticationMethod", AuthenticationType.Basic);
|
||||
return AuthenticationType.Basic;
|
||||
}
|
||||
|
||||
return GetValueEnum("AuthenticationMethod", AuthenticationType.None);
|
||||
}
|
||||
}
|
||||
|
||||
public bool AnalyticsEnabled => GetValueBoolean("AnalyticsEnabled", true, persist: false);
|
||||
|
||||
public string Branch => GetValue("Branch", "master").ToLowerInvariant();
|
||||
|
||||
public string LogLevel => GetValue("LogLevel", "info").ToLowerInvariant();
|
||||
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
|
||||
public string PostgresHost => _postgresOptions?.Host ?? GetValue("PostgresHost", string.Empty, persist: false);
|
||||
public string PostgresUser => _postgresOptions?.User ?? GetValue("PostgresUser", string.Empty, persist: false);
|
||||
public string PostgresPassword => _postgresOptions?.Password ?? GetValue("PostgresPassword", string.Empty, persist: false);
|
||||
public string PostgresMainDb => _postgresOptions?.MainDb ?? GetValue("PostgresMainDb", "radarr-main", persist: false);
|
||||
public string PostgresLogDb => _postgresOptions?.LogDb ?? GetValue("PostgresLogDb", "radarr-log", persist: false);
|
||||
public int PostgresPort => (_postgresOptions?.Port ?? 0) != 0 ? _postgresOptions.Port : GetValueInt("PostgresPort", 5432, persist: false);
|
||||
public bool LogSql => GetValueBoolean("LogSql", false, persist: false);
|
||||
public int LogRotate => GetValueInt("LogRotate", 50, persist: false);
|
||||
public bool FilterSentryEvents => GetValueBoolean("FilterSentryEvents", true, persist: false);
|
||||
public string SslCertPath => GetValue("SslCertPath", "");
|
||||
public string SslCertPassword => GetValue("SslCertPassword", "");
|
||||
|
||||
public string UrlBase
|
||||
{
|
||||
get
|
||||
{
|
||||
var urlBase = GetValue("UrlBase", "").Trim('/');
|
||||
|
||||
if (urlBase.IsNullOrWhiteSpace())
|
||||
{
|
||||
return urlBase;
|
||||
}
|
||||
|
||||
return "/" + urlBase.Trim('/').ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
public string UiFolder => BuildInfo.IsDebug ? Path.Combine("..", "UI") : "UI";
|
||||
public string InstanceName => GetValue("InstanceName", BuildInfo.AppName);
|
||||
|
||||
public bool UpdateAutomatically => GetValueBoolean("UpdateAutomatically", false, false);
|
||||
|
||||
public UpdateMechanism UpdateMechanism => GetValueEnum("UpdateMechanism", UpdateMechanism.BuiltIn, false);
|
||||
|
||||
public string UpdateScriptPath => GetValue("UpdateScriptPath", "", false);
|
||||
|
||||
public string SyslogServer => GetValue("SyslogServer", "", persist: false);
|
||||
|
||||
public int SyslogPort => GetValueInt("SyslogPort", 514, persist: false);
|
||||
|
||||
public string SyslogLevel => GetValue("SyslogLevel", LogLevel, false).ToLowerInvariant();
|
||||
|
||||
public int GetValueInt(string key, int defaultValue, bool persist = true)
|
||||
{
|
||||
return Convert.ToInt32(GetValue(key, defaultValue, persist));
|
||||
}
|
||||
|
||||
public bool GetValueBoolean(string key, bool defaultValue, bool persist = true)
|
||||
{
|
||||
return Convert.ToBoolean(GetValue(key, defaultValue, persist));
|
||||
}
|
||||
|
||||
public T GetValueEnum<T>(string key, T defaultValue, bool persist = true)
|
||||
{
|
||||
return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), persist);
|
||||
}
|
||||
|
||||
public string GetValue(string key, object defaultValue, bool persist = true)
|
||||
{
|
||||
return _cache.Get(key, () =>
|
||||
{
|
||||
var xDoc = LoadConfigFile();
|
||||
var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single();
|
||||
|
||||
var parentContainer = config;
|
||||
|
||||
var valueHolder = parentContainer.Descendants(key).ToList();
|
||||
|
||||
if (valueHolder.Count == 1)
|
||||
{
|
||||
return valueHolder.First().Value.Trim();
|
||||
}
|
||||
|
||||
//Save the value
|
||||
if (persist)
|
||||
{
|
||||
SetValue(key, defaultValue);
|
||||
}
|
||||
|
||||
//return the default value
|
||||
return defaultValue.ToString();
|
||||
});
|
||||
}
|
||||
|
||||
public void SetValue(string key, object value)
|
||||
{
|
||||
var valueString = value.ToString().Trim();
|
||||
var xDoc = LoadConfigFile();
|
||||
var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single();
|
||||
|
||||
var parentContainer = config;
|
||||
|
||||
var keyHolder = parentContainer.Descendants(key);
|
||||
|
||||
if (keyHolder.Count() != 1)
|
||||
{
|
||||
parentContainer.Add(new XElement(key, valueString));
|
||||
}
|
||||
else
|
||||
{
|
||||
parentContainer.Descendants(key).Single().Value = valueString;
|
||||
}
|
||||
|
||||
_cache.Set(key, valueString);
|
||||
|
||||
SaveConfigFile(xDoc);
|
||||
}
|
||||
|
||||
public void SetValue(string key, Enum value)
|
||||
{
|
||||
SetValue(key, value.ToString().ToLower());
|
||||
}
|
||||
|
||||
private void EnsureDefaultConfigFile()
|
||||
{
|
||||
if (!File.Exists(_configFile))
|
||||
{
|
||||
SaveConfigDictionary(GetConfigDictionary());
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteOldValues()
|
||||
{
|
||||
var xDoc = LoadConfigFile();
|
||||
var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single();
|
||||
|
||||
var type = GetType();
|
||||
var properties = type.GetProperties();
|
||||
|
||||
foreach (var configValue in config.Descendants().ToList())
|
||||
{
|
||||
var name = configValue.Name.LocalName;
|
||||
|
||||
if (!properties.Any(p => p.Name == name))
|
||||
{
|
||||
config.Descendants(name).Remove();
|
||||
}
|
||||
}
|
||||
|
||||
SaveConfigFile(xDoc);
|
||||
}
|
||||
|
||||
public XDocument LoadConfigFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (Mutex)
|
||||
{
|
||||
if (_diskProvider.FileExists(_configFile))
|
||||
{
|
||||
var contents = _diskProvider.ReadAllText(_configFile);
|
||||
|
||||
if (contents.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new InvalidConfigFileException($"{_configFile} is empty. Please delete the config file and Radarr will recreate it.");
|
||||
}
|
||||
|
||||
if (contents.All(char.IsControl))
|
||||
{
|
||||
throw new InvalidConfigFileException($"{_configFile} is corrupt. Please delete the config file and Radarr will recreate it.");
|
||||
}
|
||||
|
||||
return XDocument.Parse(_diskProvider.ReadAllText(_configFile));
|
||||
}
|
||||
|
||||
var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
|
||||
xDoc.Add(new XElement(CONFIG_ELEMENT_NAME));
|
||||
|
||||
return xDoc;
|
||||
}
|
||||
}
|
||||
catch (XmlException ex)
|
||||
{
|
||||
throw new InvalidConfigFileException($"{_configFile} is corrupt is invalid. Please delete the config file and Radarr will recreate it.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveConfigFile(XDocument xDoc)
|
||||
{
|
||||
lock (Mutex)
|
||||
{
|
||||
_diskProvider.WriteAllText(_configFile, xDoc.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private string GenerateApiKey()
|
||||
{
|
||||
return Guid.NewGuid().ToString().Replace("-", "");
|
||||
}
|
||||
|
||||
public void HandleAsync(ApplicationStartedEvent message)
|
||||
{
|
||||
EnsureDefaultConfigFile();
|
||||
DeleteOldValues();
|
||||
}
|
||||
|
||||
public void Execute(ResetApiKeyCommand message)
|
||||
{
|
||||
SetValue("ApiKey", GenerateApiKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration.Events;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.Configuration
|
||||
{
|
||||
public interface IConfigFileWriter : IHandleAsync<ApplicationStartedEvent>,
|
||||
IExecute<ResetApiKeyCommand>
|
||||
{
|
||||
public void EnsureDefaultConfigFile();
|
||||
void SaveConfigDictionary(Dictionary<string, object> configValues);
|
||||
}
|
||||
|
||||
public class ConfigFileWriter : IConfigFileWriter
|
||||
{
|
||||
public static string CONFIG_ELEMENT_NAME = BuildInfo.AppName;
|
||||
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IConfigurationRoot _configuration;
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private readonly string _configFile;
|
||||
|
||||
private static readonly object Mutex = new object();
|
||||
|
||||
public ConfigFileWriter(IAppFolderInfo appFolderInfo,
|
||||
IEventAggregator eventAggregator,
|
||||
IDiskProvider diskProvider,
|
||||
IConfiguration configuration,
|
||||
IOptionsMonitor<ConfigFileOptions> configFileOptions,
|
||||
Logger logger)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
_diskProvider = diskProvider;
|
||||
_configuration = configuration as IConfigurationRoot;
|
||||
_configFileOptions = configFileOptions;
|
||||
_logger = logger;
|
||||
|
||||
_configFile = appFolderInfo.GetConfigPath();
|
||||
|
||||
_configFileOptions.OnChange(OnChange);
|
||||
}
|
||||
|
||||
private Dictionary<string, object> GetConfigDictionary()
|
||||
{
|
||||
var dict = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
var properties = typeof(ConfigFileOptions).GetProperties();
|
||||
|
||||
foreach (var propertyInfo in properties)
|
||||
{
|
||||
var value = propertyInfo.GetValue(_configFileOptions.CurrentValue, null);
|
||||
|
||||
dict.Add(propertyInfo.Name, value);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public void SaveConfigDictionary(Dictionary<string, object> configValues)
|
||||
{
|
||||
var allWithDefaults = GetConfigDictionary();
|
||||
|
||||
var persistKeys = typeof(ConfigFileOptions).GetProperties()
|
||||
.Where(x => Attribute.IsDefined(x, typeof(PersistAttribute)))
|
||||
.Select(x => x.Name)
|
||||
.ToList();
|
||||
|
||||
foreach (var configValue in configValues)
|
||||
{
|
||||
if (configValue.Key.Equals("ApiKey", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
allWithDefaults.TryGetValue(configValue.Key, out var currentValue);
|
||||
if (currentValue == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var equal = configValue.Value.ToString().Equals(currentValue.ToString());
|
||||
var persist = persistKeys.Contains(configValue.Key);
|
||||
|
||||
if (persist || !equal)
|
||||
{
|
||||
SetValue(configValue.Key.FirstCharToUpper(), configValue.Value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(new ConfigFileSavedEvent());
|
||||
}
|
||||
|
||||
public void SetValue(string key, object value)
|
||||
{
|
||||
var valueString = value.ToString().Trim();
|
||||
var xDoc = LoadConfigFile();
|
||||
var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single();
|
||||
|
||||
var keyHolder = config.Descendants(key);
|
||||
|
||||
if (keyHolder.Count() != 1)
|
||||
{
|
||||
config.Add(new XElement(key, valueString));
|
||||
}
|
||||
else
|
||||
{
|
||||
config.Descendants(key).Single().Value = valueString;
|
||||
}
|
||||
|
||||
SaveConfigFile(xDoc);
|
||||
}
|
||||
|
||||
public void EnsureDefaultConfigFile()
|
||||
{
|
||||
if (!File.Exists(_configFile))
|
||||
{
|
||||
SaveConfigDictionary(GetConfigDictionary());
|
||||
SetValue(nameof(ConfigFileOptions.ApiKey), _configFileOptions.CurrentValue.ApiKey);
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteOldValues()
|
||||
{
|
||||
var xDoc = LoadConfigFile();
|
||||
var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single();
|
||||
|
||||
var properties = typeof(ConfigFileOptions).GetProperties();
|
||||
|
||||
foreach (var configValue in config.Descendants().ToList())
|
||||
{
|
||||
var name = configValue.Name.LocalName;
|
||||
|
||||
if (!properties.Any(p => p.Name == name))
|
||||
{
|
||||
config.Descendants(name).Remove();
|
||||
}
|
||||
}
|
||||
|
||||
SaveConfigFile(xDoc);
|
||||
}
|
||||
|
||||
public XDocument LoadConfigFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (Mutex)
|
||||
{
|
||||
if (_diskProvider.FileExists(_configFile))
|
||||
{
|
||||
var contents = _diskProvider.ReadAllText(_configFile);
|
||||
|
||||
if (contents.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new InvalidConfigFileException($"{_configFile} is empty. Please delete the config file and Radarr will recreate it.");
|
||||
}
|
||||
|
||||
if (contents.All(char.IsControl))
|
||||
{
|
||||
throw new InvalidConfigFileException($"{_configFile} is corrupt. Please delete the config file and Radarr will recreate it.");
|
||||
}
|
||||
|
||||
return XDocument.Parse(_diskProvider.ReadAllText(_configFile));
|
||||
}
|
||||
|
||||
var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
|
||||
xDoc.Add(new XElement(CONFIG_ELEMENT_NAME));
|
||||
|
||||
return xDoc;
|
||||
}
|
||||
}
|
||||
catch (XmlException ex)
|
||||
{
|
||||
throw new InvalidConfigFileException($"{_configFile} is corrupt is invalid. Please delete the config file and Radarr will recreate it.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveConfigFile(XDocument xDoc)
|
||||
{
|
||||
lock (Mutex)
|
||||
{
|
||||
_diskProvider.WriteAllText(_configFile, xDoc.ToString());
|
||||
_configuration.Reload();
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleAsync(ApplicationStartedEvent message)
|
||||
{
|
||||
DeleteOldValues();
|
||||
}
|
||||
|
||||
public void Execute(ResetApiKeyCommand message)
|
||||
{
|
||||
SetValue(nameof(ConfigFileOptions.ApiKey), new ConfigFileOptions().ApiKey);
|
||||
}
|
||||
|
||||
private void OnChange(ConfigFileOptions options)
|
||||
{
|
||||
_logger.Info("Config file updated");
|
||||
|
||||
_eventAggregator.PublishEvent(new ConfigFileSavedEvent());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.Configuration
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class PersistAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Data.SQLite;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Npgsql;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -17,25 +16,16 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
public class ConnectionStringFactory : IConnectionStringFactory
|
||||
{
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
|
||||
// Catch legacy config, to be removed soon
|
||||
private readonly PostgresOptions _postgresOptions;
|
||||
|
||||
public ConnectionStringFactory(IAppFolderInfo appFolderInfo,
|
||||
IOptionsMonitor<ConfigFileOptions> configFileOptions)
|
||||
public ConnectionStringFactory(IAppFolderInfo appFolderInfo, IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_configFileOptions = configFileOptions;
|
||||
_postgresOptions = PostgresOptions.GetOptions();
|
||||
_configFileProvider = configFileProvider;
|
||||
|
||||
var isPostgres = _configFileOptions.CurrentValue.PostgresHost.IsNotNullOrWhiteSpace() || _postgresOptions.Host.IsNotNullOrWhiteSpace();
|
||||
var mainDb = _configFileOptions.CurrentValue.PostgresMainDb ?? _postgresOptions.MainDb;
|
||||
var logDb = _configFileOptions.CurrentValue.PostgresLogDb ?? _postgresOptions.LogDb;
|
||||
|
||||
MainDbConnectionString = isPostgres ? GetPostgresConnectionString(mainDb) :
|
||||
MainDbConnectionString = _configFileProvider.PostgresHost.IsNotNullOrWhiteSpace() ? GetPostgresConnectionString(_configFileProvider.PostgresMainDb) :
|
||||
GetConnectionString(appFolderInfo.GetDatabase());
|
||||
|
||||
LogDbConnectionString = isPostgres ? GetPostgresConnectionString(logDb) :
|
||||
LogDbConnectionString = _configFileProvider.PostgresHost.IsNotNullOrWhiteSpace() ? GetPostgresConnectionString(_configFileProvider.PostgresLogDb) :
|
||||
GetConnectionString(appFolderInfo.GetLogDatabase());
|
||||
}
|
||||
|
||||
@@ -73,10 +63,10 @@ namespace NzbDrone.Core.Datastore
|
||||
var connectionBuilder = new NpgsqlConnectionStringBuilder();
|
||||
|
||||
connectionBuilder.Database = dbName;
|
||||
connectionBuilder.Host = _configFileOptions.CurrentValue.PostgresHost ?? _postgresOptions.Host;
|
||||
connectionBuilder.Username = _configFileOptions.CurrentValue.PostgresUser ?? _postgresOptions.User;
|
||||
connectionBuilder.Password = _configFileOptions.CurrentValue.PostgresPassword ?? _postgresOptions.Password;
|
||||
connectionBuilder.Port = _configFileOptions.CurrentValue.PostgresPort > 0 ? _configFileOptions.CurrentValue.PostgresPort : _postgresOptions.Port;
|
||||
connectionBuilder.Host = _configFileProvider.PostgresHost;
|
||||
connectionBuilder.Username = _configFileProvider.PostgresUser;
|
||||
connectionBuilder.Password = _configFileProvider.PostgresPassword;
|
||||
connectionBuilder.Port = _configFileProvider.PostgresPort;
|
||||
connectionBuilder.Enlist = false;
|
||||
|
||||
return connectionBuilder.ConnectionString;
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||
var moviePath = reader.GetString(3);
|
||||
var data = STJson.Deserialize<MovieCollection207>(collection);
|
||||
|
||||
if (newCollections.Any(d => d.TmdbId == data.TmdbId))
|
||||
if (data.TmdbId == 0 || newCollections.Any(d => d.TmdbId == data.TmdbId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -104,12 +104,14 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||
rootFolderPath = moviePath.GetParentPath();
|
||||
}
|
||||
|
||||
var collectionName = data.Name ?? $"Collection {data.TmdbId}";
|
||||
|
||||
newCollections.Add(new MovieCollection208
|
||||
{
|
||||
TmdbId = data.TmdbId,
|
||||
Title = data.Name,
|
||||
CleanTitle = data.Name.CleanMovieTitle(),
|
||||
SortTitle = Parser.Parser.NormalizeTitle(data.Name),
|
||||
Title = collectionName,
|
||||
CleanTitle = collectionName.CleanMovieTitle(),
|
||||
SortTitle = Parser.Parser.NormalizeTitle(collectionName),
|
||||
Added = added,
|
||||
QualityProfileId = qualityProfileId,
|
||||
RootFolderPath = rootFolderPath,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(211)]
|
||||
public class more_movie_meta_index : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Create.Index("IX_AlternativeTitles_MovieMetadataId").OnTable("AlternativeTitles").OnColumn("MovieMetadataId");
|
||||
Create.Index("IX_Credits_MovieMetadataId").OnTable("Credits").OnColumn("MovieMetadataId");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
@@ -19,7 +18,7 @@ namespace NzbDrone.Core.Datastore
|
||||
.Build();
|
||||
|
||||
var postgresOptions = new PostgresOptions();
|
||||
config.GetSection($"{BuildInfo.AppName}:Postgres").Bind(postgresOptions);
|
||||
config.GetSection("Radarr:Postgres").Bind(postgresOptions);
|
||||
|
||||
return postgresOptions;
|
||||
}
|
||||
|
||||
@@ -180,14 +180,14 @@ namespace NzbDrone.Core.Download
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug()
|
||||
_logger.ForDebugEvent()
|
||||
.Message("No Movies were just imported, but all movies were previously imported, possible issue with download history.")
|
||||
.Property("MovieId", trackedDownload.RemoteMovie.Movie.Id)
|
||||
.Property("DownloadId", trackedDownload.DownloadItem.DownloadId)
|
||||
.Property("Title", trackedDownload.DownloadItem.Title)
|
||||
.Property("Path", trackedDownload.ImportItem.OutputPath.ToString())
|
||||
.WriteSentryWarn("DownloadHistoryIncomplete")
|
||||
.Write();
|
||||
.Log();
|
||||
}
|
||||
|
||||
trackedDownload.State = TrackedDownloadState.Imported;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Download.History
|
||||
|
||||
public class DownloadHistoryService : IDownloadHistoryService,
|
||||
IHandle<MovieGrabbedEvent>,
|
||||
IHandle<MovieImportedEvent>,
|
||||
IHandle<MovieFileImportedEvent>,
|
||||
IHandle<DownloadCompletedEvent>,
|
||||
IHandle<DownloadFailedEvent>,
|
||||
IHandle<DownloadIgnoredEvent>,
|
||||
@@ -120,7 +120,7 @@ namespace NzbDrone.Core.Download.History
|
||||
_repository.Insert(history);
|
||||
}
|
||||
|
||||
public void Handle(MovieImportedEvent message)
|
||||
public void Handle(MovieFileImportedEvent message)
|
||||
{
|
||||
if (!message.NewDownload)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||
public class DownloadMonitoringService : IExecute<RefreshMonitoredDownloadsCommand>,
|
||||
IExecute<CheckForFinishedDownloadCommand>,
|
||||
IHandle<MovieGrabbedEvent>,
|
||||
IHandle<MovieImportedEvent>,
|
||||
IHandle<MovieFileImportedEvent>,
|
||||
IHandle<DownloadsProcessedEvent>,
|
||||
IHandle<TrackedDownloadsRemovedEvent>
|
||||
{
|
||||
@@ -167,7 +167,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||
_refreshDebounce.Execute();
|
||||
}
|
||||
|
||||
public void Handle(MovieImportedEvent message)
|
||||
public void Handle(MovieFileImportedEvent message)
|
||||
{
|
||||
_refreshDebounce.Execute();
|
||||
}
|
||||
|
||||
@@ -158,22 +158,32 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
||||
{
|
||||
var setRating = new XElement("ratings");
|
||||
|
||||
if (movie.MovieMetadata.Value.Ratings.Tmdb?.Votes > 0)
|
||||
{
|
||||
var setRatethemoviedb = new XElement("rating", new XAttribute("name", "themoviedb"), new XAttribute("max", "10"), new XAttribute("default", "true"));
|
||||
setRatethemoviedb.Add(new XElement("value", movie.MovieMetadata.Value.Ratings.Tmdb.Value));
|
||||
setRatethemoviedb.Add(new XElement("votes", movie.MovieMetadata.Value.Ratings.Tmdb.Votes));
|
||||
setRating.Add(setRatethemoviedb);
|
||||
}
|
||||
var defaultRatingSet = false;
|
||||
|
||||
if (movie.MovieMetadata.Value.Ratings.Imdb?.Votes > 0)
|
||||
{
|
||||
var setRateImdb = new XElement("rating", new XAttribute("name", "imdb"), new XAttribute("max", "10"));
|
||||
var setRateImdb = new XElement("rating", new XAttribute("name", "imdb"), new XAttribute("max", "10"), new XAttribute("default", "true"));
|
||||
setRateImdb.Add(new XElement("value", movie.MovieMetadata.Value.Ratings.Imdb.Value));
|
||||
setRateImdb.Add(new XElement("votes", movie.MovieMetadata.Value.Ratings.Imdb.Votes));
|
||||
|
||||
defaultRatingSet = true;
|
||||
setRating.Add(setRateImdb);
|
||||
}
|
||||
|
||||
if (movie.MovieMetadata.Value.Ratings.Tmdb?.Votes > 0)
|
||||
{
|
||||
var setRatethemoviedb = new XElement("rating", new XAttribute("name", "themoviedb"), new XAttribute("max", "10"));
|
||||
setRatethemoviedb.Add(new XElement("value", movie.MovieMetadata.Value.Ratings.Tmdb.Value));
|
||||
setRatethemoviedb.Add(new XElement("votes", movie.MovieMetadata.Value.Ratings.Tmdb.Votes));
|
||||
|
||||
if (!defaultRatingSet)
|
||||
{
|
||||
setRatethemoviedb.SetAttributeValue("default", "true");
|
||||
}
|
||||
|
||||
setRating.Add(setRatethemoviedb);
|
||||
}
|
||||
|
||||
details.Add(setRating);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using NzbDrone.Core.ThingiProvider.Events;
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
[CheckOn(typeof(ProviderUpdatedEvent<IImportList>))]
|
||||
[CheckOn(typeof(MovieImportedEvent), CheckOnCondition.FailedOnly)]
|
||||
[CheckOn(typeof(MovieFileImportedEvent), CheckOnCondition.FailedOnly)]
|
||||
[CheckOn(typeof(MovieImportFailedEvent), CheckOnCondition.SuccessfulOnly)]
|
||||
public class ImportListRootFolderCheck : HealthCheckBase
|
||||
{
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Localization;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
public class LegacyPostgresCheck : HealthCheckBase
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public LegacyPostgresCheck(ILocalizationService localizationService, Logger logger)
|
||||
: base(localizationService)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var legacyVars = Environment
|
||||
.GetEnvironmentVariables()
|
||||
.Cast<DictionaryEntry>()
|
||||
.Select(x => x.Key.ToString())
|
||||
.Where(k => k.StartsWith(BuildInfo.AppName + "__Postgres__") || k.StartsWith(BuildInfo.AppName + ":Postgres:"))
|
||||
.ToList();
|
||||
|
||||
if (legacyVars.Count == 0)
|
||||
{
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
var legacyString = legacyVars.OrderBy(x => x).ConcatToString();
|
||||
var newString = legacyString
|
||||
.Replace(BuildInfo.AppName + "__Postgres__", BuildInfo.AppName + "__Postgres")
|
||||
.Replace(BuildInfo.AppName + ":Postgres:", BuildInfo.AppName + ":Postgres");
|
||||
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
string.Format(_localizationService.GetLocalizedString("PostgresLegacyEnvironmentVariables"), legacyString, newString));
|
||||
}
|
||||
|
||||
public override bool CheckOnSchedule => false;
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,15 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Movies.Collections;
|
||||
using NzbDrone.Core.Movies.Events;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
[CheckOn(typeof(CollectionEditedEvent), CheckOnCondition.Always)]
|
||||
[CheckOn(typeof(ModelEvent<RootFolder>))]
|
||||
public class MovieCollectionRootFolderCheck : HealthCheckBase
|
||||
{
|
||||
private readonly IMovieCollectionService _collectionService;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Configuration.Events;
|
||||
using NzbDrone.Core.Localization;
|
||||
@@ -10,9 +9,9 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
[CheckOn(typeof(ConfigSavedEvent))]
|
||||
public class ReleaseBranchCheck : HealthCheckBase
|
||||
{
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileService;
|
||||
private readonly IConfigFileProvider _configFileService;
|
||||
|
||||
public ReleaseBranchCheck(IOptionsMonitor<ConfigFileOptions> configFileService, ILocalizationService localizationService)
|
||||
public ReleaseBranchCheck(IConfigFileProvider configFileService, ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
{
|
||||
_configFileService = configFileService;
|
||||
@@ -20,11 +19,11 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var currentBranch = _configFileService.CurrentValue.Branch.ToLower();
|
||||
var currentBranch = _configFileService.Branch.ToLower();
|
||||
|
||||
if (!Enum.GetNames(typeof(ReleaseBranches)).Any(x => x.ToLower() == currentBranch))
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckOfficialBranchMessage"), _configFileService.CurrentValue.Branch), "#branch-is-not-a-valid-release-branch");
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckOfficialBranchMessage"), _configFileService.Branch), "#branch-is-not-a-valid-release-branch");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
[CheckOn(typeof(ProviderUpdatedEvent<IDownloadClient>))]
|
||||
[CheckOn(typeof(ProviderDeletedEvent<IDownloadClient>))]
|
||||
[CheckOn(typeof(ModelEvent<RemotePathMapping>))]
|
||||
[CheckOn(typeof(MovieImportedEvent), CheckOnCondition.FailedOnly)]
|
||||
[CheckOn(typeof(MovieFileImportedEvent), CheckOnCondition.FailedOnly)]
|
||||
[CheckOn(typeof(MovieImportFailedEvent), CheckOnCondition.SuccessfulOnly)]
|
||||
public class RemotePathMappingCheck : HealthCheckBase, IProvideHealthCheck
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -17,13 +16,13 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IAppFolderInfo _appFolderInfo;
|
||||
private readonly ICheckUpdateService _checkUpdateService;
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IOsInfo _osInfo;
|
||||
|
||||
public UpdateCheck(IDiskProvider diskProvider,
|
||||
IAppFolderInfo appFolderInfo,
|
||||
ICheckUpdateService checkUpdateService,
|
||||
IOptionsMonitor<ConfigFileOptions> configFileOptions,
|
||||
IConfigFileProvider configFileProvider,
|
||||
IOsInfo osInfo,
|
||||
ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
@@ -31,7 +30,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
_diskProvider = diskProvider;
|
||||
_appFolderInfo = appFolderInfo;
|
||||
_checkUpdateService = checkUpdateService;
|
||||
_configFileOptions = configFileOptions;
|
||||
_configFileProvider = configFileProvider;
|
||||
_osInfo = osInfo;
|
||||
}
|
||||
|
||||
@@ -40,8 +39,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
var startupFolder = _appFolderInfo.StartUpFolder;
|
||||
var uiFolder = Path.Combine(startupFolder, "UI");
|
||||
|
||||
if ((OsInfo.IsWindows || _configFileOptions.CurrentValue.UpdateAutomatically) &&
|
||||
_configFileOptions.CurrentValue.UpdateMechanism == UpdateMechanism.BuiltIn &&
|
||||
if ((OsInfo.IsWindows || _configFileProvider.UpdateAutomatically) &&
|
||||
_configFileProvider.UpdateMechanism == UpdateMechanism.BuiltIn &&
|
||||
!_osInfo.IsDocker)
|
||||
{
|
||||
if (OsInfo.IsOsx && startupFolder.GetAncestorFolders().Contains("AppTranslocation"))
|
||||
|
||||
@@ -77,12 +77,15 @@ namespace NzbDrone.Core.HealthCheck
|
||||
.ToDictionary(g => g.Key, g => g.ToArray());
|
||||
}
|
||||
|
||||
private void PerformHealthCheck(IProvideHealthCheck[] healthChecks)
|
||||
private void PerformHealthCheck(IProvideHealthCheck[] healthChecks, bool performServerChecks = false)
|
||||
{
|
||||
var results = healthChecks.Select(c => c.Check())
|
||||
.ToList();
|
||||
|
||||
results.AddRange(_serverSideNotificationService.GetServerChecks());
|
||||
if (performServerChecks)
|
||||
{
|
||||
results.AddRange(_serverSideNotificationService.GetServerChecks());
|
||||
}
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
@@ -108,17 +111,17 @@ namespace NzbDrone.Core.HealthCheck
|
||||
{
|
||||
if (message.Trigger == CommandTrigger.Manual)
|
||||
{
|
||||
PerformHealthCheck(_healthChecks);
|
||||
PerformHealthCheck(_healthChecks, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
PerformHealthCheck(_scheduledHealthChecks);
|
||||
PerformHealthCheck(_scheduledHealthChecks, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleAsync(ApplicationStartedEvent message)
|
||||
{
|
||||
PerformHealthCheck(_startupHealthChecks);
|
||||
PerformHealthCheck(_startupHealthChecks, true);
|
||||
}
|
||||
|
||||
public void HandleAsync(IEvent message)
|
||||
|
||||
@@ -2,8 +2,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Cloud;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Http;
|
||||
@@ -20,28 +20,41 @@ namespace NzbDrone.Core.HealthCheck
|
||||
public class ServerSideNotificationService : IServerSideNotificationService
|
||||
{
|
||||
private readonly IHttpClient _client;
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ServerSideNotificationService(IHttpClient client, IOptionsMonitor<ConfigFileOptions> configFileOptions, IRadarrCloudRequestBuilder cloudRequestBuilder, Logger logger)
|
||||
private readonly ICached<List<HealthCheck>> _cache;
|
||||
|
||||
public ServerSideNotificationService(IHttpClient client,
|
||||
IConfigFileProvider configFileProvider,
|
||||
IRadarrCloudRequestBuilder cloudRequestBuilder,
|
||||
ICacheManager cacheManager,
|
||||
Logger logger)
|
||||
{
|
||||
_client = client;
|
||||
_configFileOptions = configFileOptions;
|
||||
_configFileProvider = configFileProvider;
|
||||
_cloudRequestBuilder = cloudRequestBuilder.Services;
|
||||
_logger = logger;
|
||||
|
||||
_cache = cacheManager.GetCache<List<HealthCheck>>(GetType());
|
||||
}
|
||||
|
||||
public List<HealthCheck> GetServerChecks()
|
||||
{
|
||||
return _cache.Get("ServerChecks", () => RetrieveServerChecks(), TimeSpan.FromHours(2));
|
||||
}
|
||||
|
||||
private List<HealthCheck> RetrieveServerChecks()
|
||||
{
|
||||
var request = _cloudRequestBuilder.Create()
|
||||
.Resource("/notification")
|
||||
.AddQueryParam("version", BuildInfo.Version)
|
||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
||||
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("branch", _configFileOptions.CurrentValue.Branch)
|
||||
.Build();
|
||||
.Resource("/notification")
|
||||
.AddQueryParam("version", BuildInfo.Version)
|
||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
||||
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
|
||||
.AddQueryParam("branch", _configFileProvider.Branch)
|
||||
.Build();
|
||||
try
|
||||
{
|
||||
_logger.Trace("Getting server side health notifications");
|
||||
|
||||
@@ -27,13 +27,13 @@ namespace NzbDrone.Core.History
|
||||
List<MovieHistory> FindByDownloadId(string downloadId);
|
||||
List<MovieHistory> GetByMovieId(int movieId, MovieHistoryEventType? eventType);
|
||||
void UpdateMany(List<MovieHistory> toUpdate);
|
||||
string FindDownloadId(MovieImportedEvent trackedDownload);
|
||||
string FindDownloadId(MovieFileImportedEvent trackedDownload);
|
||||
List<MovieHistory> Since(DateTime date, MovieHistoryEventType? eventType);
|
||||
}
|
||||
|
||||
public class HistoryService : IHistoryService,
|
||||
IHandle<MovieGrabbedEvent>,
|
||||
IHandle<MovieImportedEvent>,
|
||||
IHandle<MovieFileImportedEvent>,
|
||||
IHandle<DownloadFailedEvent>,
|
||||
IHandle<MovieFileDeletedEvent>,
|
||||
IHandle<MovieFileRenamedEvent>,
|
||||
@@ -97,7 +97,7 @@ namespace NzbDrone.Core.History
|
||||
_historyRepository.UpdateMany(toUpdate);
|
||||
}
|
||||
|
||||
public string FindDownloadId(MovieImportedEvent trackedDownload)
|
||||
public string FindDownloadId(MovieFileImportedEvent trackedDownload)
|
||||
{
|
||||
_logger.Debug("Trying to find downloadId for {0} from history", trackedDownload.ImportedMovie.Path);
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace NzbDrone.Core.History
|
||||
_historyRepository.Insert(history);
|
||||
}
|
||||
|
||||
public void Handle(MovieImportedEvent message)
|
||||
public void Handle(MovieFileImportedEvent message)
|
||||
{
|
||||
if (!message.NewDownload)
|
||||
{
|
||||
|
||||
@@ -16,12 +16,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
{
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM ""Collections""
|
||||
WHERE ""TmdbId"" IN (
|
||||
SELECT ""Collections"".""TmdbId"" FROM ""Collections""
|
||||
LEFT OUTER JOIN ""MovieMetadata""
|
||||
ON ""Collections"".""TmdbId"" = ""MovieMetadata"".""CollectionTmdbId""
|
||||
WHERE ""MovieMetadata"".""Id"" IS NULL)");
|
||||
mapper.Execute(@"DELETE FROM ""Collections"" WHERE ""TmdbId"" IN (SELECT ""X"".""TmdbId"" FROM (SELECT ""Collections"".""TmdbId"", COUNT(""Movies"".""Id"") as ""MovieCount"" FROM ""Collections""
|
||||
LEFT OUTER JOIN ""MovieMetadata"" ON ""Collections"".""TmdbId"" = ""MovieMetadata"".""CollectionTmdbId""
|
||||
LEFT OUTER JOIN ""Movies"" ON ""Movies"".""MovieMetadataId"" = ""MovieMetadata"".""Id""
|
||||
GROUP BY ""Collections"".""Id"") AS ""X"" WHERE ""X"".""MovieCount"" = 0)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Targets.Syslog;
|
||||
@@ -18,21 +17,21 @@ namespace NzbDrone.Core.Instrumentation
|
||||
{
|
||||
public class ReconfigureLogging : IHandleAsync<ConfigFileSavedEvent>
|
||||
{
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
|
||||
public ReconfigureLogging(IOptionsMonitor<ConfigFileOptions> configFileOptions)
|
||||
public ReconfigureLogging(IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_configFileOptions = configFileOptions;
|
||||
_configFileProvider = configFileProvider;
|
||||
}
|
||||
|
||||
public void Reconfigure()
|
||||
{
|
||||
var minimumLogLevel = LogLevel.FromString(_configFileOptions.CurrentValue.LogLevel);
|
||||
var minimumLogLevel = LogLevel.FromString(_configFileProvider.LogLevel);
|
||||
LogLevel minimumConsoleLogLevel;
|
||||
|
||||
if (_configFileOptions.CurrentValue.ConsoleLogLevel.IsNotNullOrWhiteSpace())
|
||||
if (_configFileProvider.ConsoleLogLevel.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
minimumConsoleLogLevel = LogLevel.FromString(_configFileOptions.CurrentValue.ConsoleLogLevel);
|
||||
minimumConsoleLogLevel = LogLevel.FromString(_configFileProvider.ConsoleLogLevel);
|
||||
}
|
||||
else if (minimumLogLevel > LogLevel.Info)
|
||||
{
|
||||
@@ -43,10 +42,10 @@ namespace NzbDrone.Core.Instrumentation
|
||||
minimumConsoleLogLevel = LogLevel.Info;
|
||||
}
|
||||
|
||||
if (_configFileOptions.CurrentValue.SyslogServer.IsNotNullOrWhiteSpace())
|
||||
if (_configFileProvider.SyslogServer.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var syslogLevel = LogLevel.FromString(_configFileOptions.CurrentValue.SyslogLevel);
|
||||
SetSyslogParameters(_configFileOptions.CurrentValue.SyslogServer, _configFileOptions.CurrentValue.SyslogPort, syslogLevel);
|
||||
var syslogLevel = LogLevel.FromString(_configFileProvider.SyslogLevel);
|
||||
SetSyslogParameters(_configFileProvider.SyslogServer, _configFileProvider.SyslogPort, syslogLevel);
|
||||
}
|
||||
|
||||
var rules = LogManager.Configuration.LoggingRules;
|
||||
@@ -61,7 +60,7 @@ namespace NzbDrone.Core.Instrumentation
|
||||
SetLogRotation();
|
||||
|
||||
//Log Sql
|
||||
SqlBuilderExtensions.LogSql = _configFileOptions.CurrentValue.LogSql;
|
||||
SqlBuilderExtensions.LogSql = _configFileProvider.LogSql;
|
||||
|
||||
//Sentry
|
||||
ReconfigureSentry();
|
||||
@@ -96,7 +95,7 @@ namespace NzbDrone.Core.Instrumentation
|
||||
{
|
||||
foreach (var target in LogManager.Configuration.AllTargets.OfType<NzbDroneFileTarget>())
|
||||
{
|
||||
target.MaxArchiveFiles = _configFileOptions.CurrentValue.LogRotate;
|
||||
target.MaxArchiveFiles = _configFileProvider.LogRotate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,8 +104,8 @@ namespace NzbDrone.Core.Instrumentation
|
||||
var sentryTarget = LogManager.Configuration.AllTargets.OfType<SentryTarget>().FirstOrDefault();
|
||||
if (sentryTarget != null)
|
||||
{
|
||||
sentryTarget.SentryEnabled = (RuntimeInfo.IsProduction && _configFileOptions.CurrentValue.AnalyticsEnabled) || RuntimeInfo.IsDevelopment;
|
||||
sentryTarget.FilterEvents = _configFileOptions.CurrentValue.FilterSentryEvents;
|
||||
sentryTarget.SentryEnabled = (RuntimeInfo.IsProduction && _configFileProvider.AnalyticsEnabled) || RuntimeInfo.IsDevelopment;
|
||||
sentryTarget.FilterEvents = _configFileProvider.FilterSentryEvents;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,9 +117,8 @@ namespace NzbDrone.Core.Instrumentation
|
||||
syslogTarget.MessageSend.Protocol = ProtocolType.Udp;
|
||||
syslogTarget.MessageSend.Udp.Port = syslogPort;
|
||||
syslogTarget.MessageSend.Udp.Server = syslogServer;
|
||||
syslogTarget.MessageSend.Udp.ReconnectInterval = 500;
|
||||
syslogTarget.MessageCreation.Rfc = RfcNumber.Rfc5424;
|
||||
syslogTarget.MessageCreation.Rfc5424.AppName = _configFileOptions.CurrentValue.InstanceName;
|
||||
syslogTarget.MessageCreation.Rfc5424.AppName = _configFileProvider.InstanceName;
|
||||
|
||||
var loggingRule = new LoggingRule("*", minimumLogLevel, syslogTarget);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Instrumentation.Sentry;
|
||||
@@ -12,15 +11,15 @@ namespace NzbDrone.Core.Instrumentation
|
||||
{
|
||||
public class ReconfigureSentry : IHandleAsync<ApplicationStartedEvent>
|
||||
{
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IPlatformInfo _platformInfo;
|
||||
private readonly IMainDatabase _database;
|
||||
|
||||
public ReconfigureSentry(IOptionsMonitor<ConfigFileOptions> configFileOptions,
|
||||
public ReconfigureSentry(IConfigFileProvider configFileProvider,
|
||||
IPlatformInfo platformInfo,
|
||||
IMainDatabase database)
|
||||
{
|
||||
_configFileOptions = configFileOptions;
|
||||
_configFileProvider = configFileProvider;
|
||||
_platformInfo = platformInfo;
|
||||
_database = database;
|
||||
}
|
||||
@@ -31,7 +30,7 @@ namespace NzbDrone.Core.Instrumentation
|
||||
var sentryTarget = LogManager.Configuration.AllTargets.OfType<SentryTarget>().FirstOrDefault();
|
||||
if (sentryTarget != null)
|
||||
{
|
||||
sentryTarget.UpdateScope(_database.Version, _database.Migration, _configFileOptions.CurrentValue.Branch, _platformInfo);
|
||||
sentryTarget.UpdateScope(_database.Version, _database.Migration, _configFileProvider.Branch, _platformInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
"IndexerRssHealthCheckNoIndexers": "Da keine Indexer mit aktivierter RSS-Synchronisierung verfügbar sind, erfasst Radarr nicht automatisch neue Releases auf",
|
||||
"IndexerSearchCheckNoAutomaticMessage": "Keine Indexer mit aktivierter automatischer Suche verfügbar. Radarr liefert keine automatischen Suchergebnisse",
|
||||
"IndexerSearchCheckNoAvailableIndexersMessage": "Alle suchfähigen Indexer sind aufgrund der kürzlichen Indexerfehler vorübergehend nicht verfügbar",
|
||||
"IndexerSearchCheckNoInteractiveMessage": "Keine Indexer mit interaktiver Suche aktiviert, Radarr liefert keine interaktiven Suchergebnisse",
|
||||
"IndexerSearchCheckNoInteractiveMessage": "Keine Indexer mit aktivierter interaktiver Suche verfügbar, Radarr liefert keine interaktiven Suchergebnisse",
|
||||
"IndexerStatusCheckAllClientMessage": "Alle Indexer sind aufgrund von Fehlern nicht verfügbar",
|
||||
"IndexerStatusCheckSingleClientMessage": "Indexer aufgrund von Fehlern nicht verfügbar: {0}",
|
||||
"Indexers": "Indexer",
|
||||
@@ -506,7 +506,7 @@
|
||||
"ScriptPath": "Script Pfad",
|
||||
"SetPermissions": "Rechte setzen",
|
||||
"SetPermissionsLinuxHelpTextWarning": "Ändere diese Einstellung nur, wenn du weißt was sie bewirken.",
|
||||
"ShouldMonitorHelpText": "Beobachte Filme die von dieser Liste hinzugefügt wurden",
|
||||
"ShouldMonitorHelpText": "Sollen von dieser Liste hinzugefügte Filme oder Sammlungen als beobachtet hinzugefügt werden",
|
||||
"ShowAsAllDayEvents": "Als Ganztags Events anzeigen",
|
||||
"ShowCutoffUnmetIconHelpText": "Symbol zeigen wenn die Qualitätsschwelle noch nicht erreicht wurde",
|
||||
"ShowMovieInformationHelpText": "Genre und Zertifizierung anzeigen",
|
||||
@@ -633,7 +633,7 @@
|
||||
"ShowGenres": "Genres anzeigen",
|
||||
"ShowCertification": "Zertifikation anzeigen",
|
||||
"SettingsRuntimeFormat": "Laufzeit Format",
|
||||
"SearchOnAddHelpText": "Suche nach den Filmen auf der Liste nach dem hinzufügen",
|
||||
"SearchOnAddHelpText": "Nach Filmen auf dieser Liste suchen, wenn sie der Bibliothek hinzugefügt werden",
|
||||
"RSSSyncIntervalHelpTextWarning": "Dies wird alle Indexer betreffen. Bitte folge deren Regeln",
|
||||
"RSSIsNotSupportedWithThisIndexer": "RSS wird von diesem Indexer nicht unterstützt",
|
||||
"RetryingDownloadInterp": "Herunterladen nochmal versuchen {0} um {1}",
|
||||
@@ -1116,5 +1116,30 @@
|
||||
"Collections": "Sammlung",
|
||||
"MonitorMovies": "Film beobachten",
|
||||
"NoCollections": "Keine Filme gefunden. Zum Starten solltest du einen Film hinzufügen oder vorhandene Importieren.",
|
||||
"RssSyncHelpText": "Intervall in Minuten. Zum deaktivieren auf 0 setzen ( Dies wird das automatische Release erfassen deaktivieren )"
|
||||
"RssSyncHelpText": "Intervall in Minuten. Zum deaktivieren auf 0 setzen ( Dies wird das automatische Release erfassen deaktivieren )",
|
||||
"CollectionOptions": "Sammlung Optionen",
|
||||
"ChooseImportMode": "Wähle eine Importmethode",
|
||||
"CollectionsSelectedInterp": "{0} Ausgewählte Sammlung(en)",
|
||||
"MovieCollectionMissingRoot": "Fehlender Stammordner für die Filmsammlung: {0}",
|
||||
"EditCollection": "Sammlung bearbeiten",
|
||||
"MonitoredCollectionHelpText": "Beobachten zur automatischen Aufnahme von Filmen aus dieser Sammlung in die Bibliothek",
|
||||
"MovieOnly": "Nur Film",
|
||||
"ScrollMovies": "Filme scrollen",
|
||||
"ShowCollectionDetails": "Status der Sammlung anzeigen",
|
||||
"RefreshCollections": "Sammlungen aktualisieren",
|
||||
"RefreshMonitoredIntervalHelpText": "Wie häufig die beobachteten Downloads von Download-Clients aktualisiert werden sollen (Min. 1 Minute).",
|
||||
"ShowOverview": "Übersicht anzeigen",
|
||||
"ShowPosters": "Plakate anzeigen",
|
||||
"CollectionShowDetailsHelpText": "Status und Eigenschaften der Sammlung anzeigen",
|
||||
"CollectionShowPostersHelpText": "Poster der Sammlungseinträge zeigen",
|
||||
"SearchOnAddCollectionHelpText": "Nach Filmen in dieser Sammlung suchen, wenn sie der Bibliothek hinzugefügt werden",
|
||||
"CollectionShowOverviewsHelpText": "Sammlungsübersichten anzeigen",
|
||||
"MonitorCollection": "Sammlung beobachten",
|
||||
"MovieAndCollection": "Film und Sammlung",
|
||||
"MovieCollectionMultipleMissingRoots": "Es fehlen mehrere Stammordner für Filmsammlungen: {0}",
|
||||
"OnMovieAdded": "Bei Film hinzugefügt",
|
||||
"OnMovieAddedHelpText": "Ausführen wenn ein Film hinzugefügt wurde",
|
||||
"UnableToLoadCollections": "Sammlungen können nicht geladen werden",
|
||||
"InstanceName": "Instanzname",
|
||||
"InstanceNameHelpText": "Instanzname im Browser-Tab und für Syslog-Anwendungsname"
|
||||
}
|
||||
|
||||
@@ -696,7 +696,6 @@
|
||||
"PosterOptions": "Poster Options",
|
||||
"Posters": "Posters",
|
||||
"PosterSize": "Poster Size",
|
||||
"PostgresLegacyEnvironmentVariables": "You have defined the following legacy PostgreSQL environment variables: {0}. Please update them to: {1}",
|
||||
"PreferAndUpgrade": "Prefer and Upgrade",
|
||||
"PreferIndexerFlags": "Prefer Indexer Flags",
|
||||
"PreferIndexerFlagsHelpText": "Prioritize releases with special flags",
|
||||
@@ -860,6 +859,7 @@
|
||||
"RootFolderCheckMultipleMessage": "Multiple root folders are missing: {0}",
|
||||
"RootFolderCheckSingleMessage": "Missing root folder: {0}",
|
||||
"RootFolders": "Root Folders",
|
||||
"RottenTomatoesRating": "Tomato Rating",
|
||||
"RSS": "RSS",
|
||||
"RSSIsNotSupportedWithThisIndexer": "RSS is not supported with this indexer",
|
||||
"RSSSync": "RSS Sync",
|
||||
|
||||
@@ -1115,5 +1115,9 @@
|
||||
"AllCollectionsHiddenDueToFilter": "Todas las películas están ocultas debido al filtro aplicado.",
|
||||
"Collections": "Colección",
|
||||
"MonitorMovies": "Monitorear Película",
|
||||
"NoCollections": "No se encontraron películas, para comenzar, querrá agregar una nueva película o importar algunas existentes."
|
||||
"NoCollections": "No se encontraron películas, para comenzar, querrá agregar una nueva película o importar algunas existentes.",
|
||||
"ChooseImportMode": "Elegir Modo de Importación",
|
||||
"CollectionOptions": "Opciones de la Colección",
|
||||
"CollectionShowDetailsHelpText": "Mostrar el estado y propiedades de la colección",
|
||||
"CollectionShowOverviewsHelpText": "Mostrar resumenes de la colección"
|
||||
}
|
||||
|
||||
@@ -1115,7 +1115,7 @@
|
||||
"RefreshMonitoredIntervalHelpText": "Miten usein valvottujen latausten tiedot päivitetään lataustyökaluilta (vähimmäisaika on 1 minuutti).",
|
||||
"RssSyncHelpText": "Aikaväli minuutteina. Arvo nolla kytkee toiminnon pois käytöstä ja lopettaen samalla automaattisen julkaisujen kaappauksen täysin.",
|
||||
"InstanceName": "Instanssin nimi",
|
||||
"InstanceNameHelpText": "Instanssin nimi välilehdellä ja sovelluksen Syslog-nimeksi",
|
||||
"InstanceNameHelpText": "Instanssin nimi välilehdellä ja järjestelmälokissa",
|
||||
"AllCollectionsHiddenDueToFilter": "Käytössä oleva suodatin on piilottanut kaikki kokoelmat.",
|
||||
"Collections": "Kokoelmat",
|
||||
"MonitorMovies": "Valvo elokuvia",
|
||||
@@ -1141,5 +1141,6 @@
|
||||
"CollectionShowOverviewsHelpText": "Näytä kokoelmien katsaukset",
|
||||
"CollectionOptions": "Kokoelmien valinnat",
|
||||
"CollectionShowDetailsHelpText": "Näytä kokoelmien tila ja ominaisuudet",
|
||||
"CollectionShowPostersHelpText": "Näytä kokoelmien julisteet"
|
||||
"CollectionShowPostersHelpText": "Näytä kokoelmien julisteet",
|
||||
"RottenTomatoesRating": "Tomaattiarvio"
|
||||
}
|
||||
|
||||
@@ -1137,5 +1137,10 @@
|
||||
"CollectionOptions": "Gyűjtemény baállítások",
|
||||
"CollectionShowOverviewsHelpText": "Gyűjtemények áttekintésének megjelenítése",
|
||||
"CollectionShowDetailsHelpText": "A gyűjtemény állapotának és tulajdonságainak megjelenítése",
|
||||
"CollectionShowPostersHelpText": "Gyűjteményelemek posztereinek megjelenítése"
|
||||
"CollectionShowPostersHelpText": "Gyűjteményelemek posztereinek megjelenítése",
|
||||
"OnMovieAdded": "Film hozzáadásakor",
|
||||
"OnMovieAddedHelpText": "Film hozzáadásakor",
|
||||
"ShowPosters": "Poszterek megjelenítése",
|
||||
"InstanceNameHelpText": "Példánynév a böngésző lapon és a syslog alkalmazás neve",
|
||||
"RottenTomatoesRating": "Tomato Értékelés"
|
||||
}
|
||||
|
||||
1
src/NzbDrone.Core/Localization/Core/lt.json
Normal file
1
src/NzbDrone.Core/Localization/Core/lt.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -177,5 +177,82 @@
|
||||
"ColonReplacementFormatHelpText": "Endre hvordan Radarr håndterer kolonerstatning",
|
||||
"ConnectionLostAutomaticMessage": "Radarr vil forsøke å koble til automatisk, eller du kan klikke oppdater nedenfor.",
|
||||
"ConnectSettingsSummary": "Varslinger, tilkoblinger til mediaservere/avspillere, og tilpassede scripts",
|
||||
"CopyUsingHardlinksHelpTextWarning": "I blant kan låste filer forhindre å endre navn på filer som blir delt. Du kan midlertidig deaktivere deling og bruke Raddars navnefunksjon for å jobbe rundt dette."
|
||||
"CopyUsingHardlinksHelpTextWarning": "I blant kan låste filer forhindre å endre navn på filer som blir delt. Du kan midlertidig deaktivere deling og bruke Raddars navnefunksjon for å jobbe rundt dette.",
|
||||
"Movies": "Film",
|
||||
"DeleteCustomFormat": "Klon egendefinert format",
|
||||
"Deleted": "Slett",
|
||||
"Indexers": "Indeksere",
|
||||
"Queued": "Kø",
|
||||
"Title": "Tittel",
|
||||
"Updates": "Oppdater",
|
||||
"Lists": "Liste",
|
||||
"Search": "Søk",
|
||||
"Titles": "Tittel",
|
||||
"Password": "Passord",
|
||||
"Peers": "Likemenn",
|
||||
"Port": "Port",
|
||||
"Posters": "Plakater",
|
||||
"Protocol": "Protokoll",
|
||||
"Quality": "kvalitet",
|
||||
"QualityProfile": "Kvaltietsprofil",
|
||||
"Ratings": "Vurdering",
|
||||
"Refresh": "Oppdater",
|
||||
"Reload": "Likemenn",
|
||||
"Remove": "Slett",
|
||||
"Replace": "Erstatt",
|
||||
"Required": "Kreve",
|
||||
"Restrictions": "Begrensning",
|
||||
"RootFolder": "Rotmappe",
|
||||
"RootFolders": "Rotmappe",
|
||||
"Runtime": "Kjøretid",
|
||||
"Scheduled": "Planlagt",
|
||||
"Seeders": "Delere",
|
||||
"Settings": "Innstillinger",
|
||||
"SettingsRemotePathMappingLocalPath": "Lokal sti",
|
||||
"Torrents": "Torrents",
|
||||
"Trakt": "Trakt",
|
||||
"URLBase": "URL Base",
|
||||
"Username": "Brukernavn",
|
||||
"Delete": "Slett",
|
||||
"Info": "Info",
|
||||
"LocalPath": "Lokal sti",
|
||||
"Usenet": "Usenet",
|
||||
"QualityProfiles": "Kvaltietsprofil",
|
||||
"Queue": "Kø",
|
||||
"Rating": "Vurdering",
|
||||
"UI": "Grensesnitt",
|
||||
"UpdateMechanismHelpText": "Bruk Prowlarrs innebygde oppdaterer eller et skript",
|
||||
"RSS": "RSS",
|
||||
"DelayProfile": "Utsetningsprofil",
|
||||
"DelayProfiles": "Utsetningsprofil",
|
||||
"Enable": "Aktiver",
|
||||
"Enabled": "Aktiver",
|
||||
"Events": "Hendelse",
|
||||
"Files": "Fil",
|
||||
"Filter": "Filter",
|
||||
"Filters": "Filtre",
|
||||
"Formats": "Format",
|
||||
"Grab": "Hent",
|
||||
"Host": "Vert",
|
||||
"Hostname": "Vertsnavn",
|
||||
"ICalFeed": "iCal Feed",
|
||||
"iCalLink": "iCal Link",
|
||||
"IMDb": "IMDb",
|
||||
"Indexer": "Indekser",
|
||||
"Metadata": "metadata",
|
||||
"Movie": "Film",
|
||||
"Crew": "Besetning",
|
||||
"Details": "detaljer",
|
||||
"RecyclingBin": "Papirkurv",
|
||||
"Custom": "Tilpass",
|
||||
"CustomFormat": "Egendefinert format",
|
||||
"CustomFormats": "Egendefinert format",
|
||||
"Cutoff": "Avskjæring",
|
||||
"Disabled": "deaktivert",
|
||||
"DownloadClient": "Nedlastingsklient",
|
||||
"DownloadClients": "Nedlastingsklient",
|
||||
"Language": "språk",
|
||||
"List": "Liste",
|
||||
"New": "Ny",
|
||||
"RemotePathMappings": "Ekstern portmapping"
|
||||
}
|
||||
|
||||
@@ -246,10 +246,10 @@
|
||||
"ImportMechanismHealthCheckMessage": "Włącz obsługę ukończonego pobierania",
|
||||
"IndexersSettingsSummary": "Indeksatory i ograniczenia dotyczące wersji",
|
||||
"InvalidFormat": "Niepoprawny format",
|
||||
"ChmodFolder": "chmod Folder",
|
||||
"ChmodFolder": "chmod Folderu",
|
||||
"ChmodFolderHelpText": "Ósemkowy, stosowany podczas importu / zmiany nazwy do folderów multimedialnych i plików (bez bitów wykonania)",
|
||||
"ChmodFolderHelpTextWarning": "Działa to tylko wtedy, gdy użytkownik uruchamiający Radarr jest właścicielem pliku. Lepiej jest upewnić się, że klient pobierania prawidłowo ustawia uprawnienia.",
|
||||
"ChmodGroup": "chmod Group",
|
||||
"ChmodGroup": "chmod Grupy",
|
||||
"ChmodGroupHelpText": "Nazwa grupy lub identyfikator. Użyj gid dla zdalnych systemów plików.",
|
||||
"ChmodGroupHelpTextWarning": "Działa to tylko wtedy, gdy użytkownik uruchamiający Radarr jest właścicielem pliku. Lepiej jest upewnić się, że klient pobierania używa tej samej grupy co Radarr.",
|
||||
"ListSyncLevelHelpTextWarning": "Pliki filmowe zostaną trwale usunięte, co może spowodować wyczyszczenie biblioteki, jeśli listy są puste",
|
||||
@@ -259,7 +259,7 @@
|
||||
"Medium": "Średni",
|
||||
"Minutes": "Minuty",
|
||||
"ListExclusions": "Wykluczenia z listy",
|
||||
"MissingFromDisk": "Radarr nie mógł znaleźć pliku na dysku, więc został odłączony od filmu w bazie danych.",
|
||||
"MissingFromDisk": "Radarr nie mógł znaleźć pliku na dysku, więc został odłączony od filmu w bazie danych",
|
||||
"Monday": "poniedziałek",
|
||||
"MoveFiles": "Przenieś pliki",
|
||||
"MovieIsRecommend": "Film jest zalecany na podstawie niedawnego dodania",
|
||||
@@ -484,8 +484,8 @@
|
||||
"ClickToChangeMovie": "Kliknij, aby zmienić film",
|
||||
"ClickToChangeQuality": "Kliknij, aby zmienić jakość",
|
||||
"CloneCustomFormat": "Klonuj format niestandardowy",
|
||||
"CloneFormatTag": "Clone Format Tag",
|
||||
"CloneIndexer": "Clone Indexer",
|
||||
"CloneFormatTag": "Klonuj Tag Formatu",
|
||||
"CloneIndexer": "Sklonuj Indekser",
|
||||
"CloneProfile": "Klonuj profil",
|
||||
"Close": "Blisko",
|
||||
"ColonReplacement": "Wymiana okrężnicy",
|
||||
@@ -497,7 +497,7 @@
|
||||
"Connection": "Połączenie",
|
||||
"ConnectionLost": "Utracono połączenie",
|
||||
"ConnectionLostAutomaticMessage": "Radarr spróbuje połączyć się automatycznie lub możesz kliknąć przycisk przeładuj poniżej.",
|
||||
"ConnectionLostMessage": "Radarr utracił połączenie z zapleczem i będzie musiał zostać ponownie załadowany, aby przywrócić funkcjonalność.",
|
||||
"ConnectionLostMessage": "Radarr utracił połączenie z silnikiem programu, aby przywrócić funkcjonalność musi zostać zrestartowany.",
|
||||
"Connections": "Znajomości",
|
||||
"ConnectSettings": "Ustawienia połączenia",
|
||||
"ConnectSettingsSummary": "Powiadomienia, połączenia z serwerami / odtwarzaczami multimedialnymi i niestandardowe skrypty",
|
||||
@@ -545,7 +545,7 @@
|
||||
"DeleteRestrictionHelpText": "Czy na pewno chcesz usunąć to ograniczenie?",
|
||||
"DeleteSelectedMovie": "Usuń wybrane filmy",
|
||||
"DeleteTagMessageText": "Czy na pewno chcesz usunąć tag „{0}”?",
|
||||
"DeleteTheMovieFolder": "Folder filmów „{0}” i cała jego zawartość zostaną usunięte.",
|
||||
"DeleteTheMovieFolder": "Folder '{0}' i cała jego zawartość zostaną usunięte.",
|
||||
"DestinationPath": "Ścieżka docelowa",
|
||||
"DestinationRelativePath": "Względna ścieżka celu",
|
||||
"DetailedProgressBar": "Szczegółowy pasek postępu",
|
||||
@@ -570,7 +570,7 @@
|
||||
"DownloadFailed": "Pobieranie nie udane",
|
||||
"DownloadFailedInterp": "Pobieranie nie powiodło się: {0}",
|
||||
"Downloading": "Ściąganie",
|
||||
"DownloadPropersAndRepacks": "Propers and Repacks",
|
||||
"DownloadPropersAndRepacks": "Poprawione i przepakowane",
|
||||
"DownloadPropersAndRepacksHelpTextWarning": "Użyj niestandardowych formatów do automatycznej aktualizacji do Propers / Repacks",
|
||||
"DownloadWarningCheckDownloadClientForMoreDetails": "Ostrzeżenie dotyczące pobierania: sprawdź klienta pobierania, aby uzyskać więcej informacji",
|
||||
"Edition": "Wydanie",
|
||||
@@ -638,8 +638,8 @@
|
||||
"GoToInterp": "Idź do {0}",
|
||||
"Grab": "Chwycić",
|
||||
"Grabbed": "Złapał",
|
||||
"GrabID": "Grab ID",
|
||||
"GrabRelease": "Grab Release",
|
||||
"GrabID": "Pobierz ID",
|
||||
"GrabRelease": "Pobierz Wydanie",
|
||||
"GrabReleaseMessageText": "Radarr nie był w stanie określić, dla którego filmu jest to wydanie. Radarr może nie być w stanie automatycznie zaimportować tej wersji. Czy chcesz złapać „{0}”?",
|
||||
"GrabSelected": "Wybierz wybrane",
|
||||
"Group": "Grupa",
|
||||
@@ -650,7 +650,7 @@
|
||||
"HideAdvanced": "Ukryj zaawansowane",
|
||||
"History": "Historia",
|
||||
"Host": "Gospodarz",
|
||||
"iCalLink": "iCal Link",
|
||||
"iCalLink": "Łącze do iCal",
|
||||
"IconForCutoffUnmet": "Ikona Cutoff Unmet",
|
||||
"IgnoredAddresses": "Ignorowane adresy",
|
||||
"IgnoreDeletedMovies": "Nie monitoruj usuniętych filmów",
|
||||
@@ -669,7 +669,7 @@
|
||||
"IncludeUnknownMovieItemsHelpText": "Pokaż elementy bez filmu w kolejce. Może to obejmować usunięte filmy lub cokolwiek innego w kategorii Radarr",
|
||||
"ImportMovies": "Importuj filmy",
|
||||
"IndexerPriority": "Priorytet indeksatora",
|
||||
"IndexerPriorityHelpText": "Priorytet indeksera od 1 (najwyższy) do 50 (najniższy). Domyślnie: 25. Używany podczas pobierania wydań przy wystąpieniu równoważnych wydań. Przy synchronizacji RSS i wyszukiwaniu, Radarr wciąż będzie korzystał ze wszystkich indekserów.",
|
||||
"IndexerPriorityHelpText": "Priorytet indeksera od 1 (najwyższy) do 50 (najniższy). Domyślnie: 25. Używany podczas pobierania wydań przy wystąpieniu równoważnych wydań. Przy synchronizacji RSS i wyszukiwaniu, Radarr wciąż będzie korzystał ze wszystkich indekserów",
|
||||
"Indexers": "Indeksatory",
|
||||
"IndexerSearchCheckNoAutomaticMessage": "Brak dostępnych indeksatorów z włączoną funkcją automatycznego wyszukiwania, Radarr nie zapewni żadnych automatycznych wyników wyszukiwania",
|
||||
"IndexerSearchCheckNoAvailableIndexersMessage": "Wszystkie indeksatory z możliwością wyszukiwania są tymczasowo niedostępne z powodu ostatnich błędów indeksatora",
|
||||
@@ -701,7 +701,7 @@
|
||||
"Logs": "Dzienniki",
|
||||
"LookingForReleaseProfiles1": "Szukasz profili wersji? Próbować",
|
||||
"LookingForReleaseProfiles2": "zamiast.",
|
||||
"LowerCase": "Małe litery",
|
||||
"LowerCase": "Z małej litery",
|
||||
"ManualImportSelectLanguage": "Import ręczny - wybierz język",
|
||||
"ManualImportSelectMovie": "Import ręczny - wybierz film",
|
||||
"MappedDrivesRunningAsService": "Zmapowane dyski sieciowe nie są dostępne, gdy działają jako usługa systemu Windows. Więcej informacji można znaleźć w FAQ",
|
||||
@@ -740,7 +740,7 @@
|
||||
"MovieInvalidFormat": "Film: nieprawidłowy format",
|
||||
"MultiLanguage": "Wielojęzyczny",
|
||||
"Negate": "Negować",
|
||||
"Negated": "Negated",
|
||||
"Negated": "Zanegowane",
|
||||
"NoListRecommendations": "Nie znaleziono żadnych pozycji na liście ani rekomendacji. Aby rozpocząć, musisz dodać nowy film, zaimportować istniejące lub dodać listę.",
|
||||
"NoTagsHaveBeenAddedYet": "Żadne tagi nie zostały jeszcze dodane",
|
||||
"Options": "Opcje",
|
||||
@@ -828,16 +828,16 @@
|
||||
"RSSSync": "Synchronizacja RSS",
|
||||
"RSSSyncInterval": "Częstotliwość synchronizacji RSS",
|
||||
"RSSSyncIntervalHelpTextWarning": "Dotyczy to wszystkich indeksujących, prosimy o przestrzeganie zasad przez nich określonych",
|
||||
"Runtime": "Runtime",
|
||||
"Runtime": "Długość",
|
||||
"Save": "Zapisać",
|
||||
"SaveChanges": "Zapisz zmiany",
|
||||
"SaveSettings": "Zapisz ustawienia",
|
||||
"Scheduled": "Planowy",
|
||||
"Score": "Wynik",
|
||||
"Script": "Scenariusz",
|
||||
"ScriptPath": "Script Path",
|
||||
"ScriptPath": "Ścieżka do Skryptu",
|
||||
"SearchAll": "Wyszukaj wszystko",
|
||||
"SearchCutoffUnmet": "Search Cutoff Unmet",
|
||||
"SearchCutoffUnmet": "Niespełnione Parametry Wyszukiwania",
|
||||
"SearchFailedPleaseTryAgainLater": "Wyszukiwanie nie powiodło się, spróbuj ponownie później.",
|
||||
"SearchFiltered": "Szukaj przefiltrowane",
|
||||
"SearchForMissing": "Wyszukaj brakujące",
|
||||
@@ -893,7 +893,7 @@
|
||||
"Shutdown": "Zamknąć",
|
||||
"SizeOnDisk": "Rozmiar dysku",
|
||||
"SkipFreeSpaceCheck": "Pomiń sprawdzanie wolnego miejsca",
|
||||
"SkipFreeSpaceCheckWhenImportingHelpText": "Użyj, gdy Radarr nie może wykryć wolnego miejsca w folderze głównym filmu",
|
||||
"SkipFreeSpaceCheckWhenImportingHelpText": "Zaznacz, jeśli Radarr nie będzie wstanie wykryć ilości wolnego miejsca w główny folderze filmów",
|
||||
"Small": "Mały",
|
||||
"Socks4": "Skarpetki 4",
|
||||
"Socks5": "Socks5 (Wsparcie TOR)",
|
||||
@@ -932,10 +932,10 @@
|
||||
"Time": "Czas",
|
||||
"Title": "Tytuł",
|
||||
"Titles": "Tytuły",
|
||||
"TMDBId": "TMDb Id",
|
||||
"TMDBId": "Identyfikator TMDb",
|
||||
"TmdbIdHelpText": "Identyfikator TMDb filmu do wykluczenia",
|
||||
"Today": "Dzisiaj",
|
||||
"TorrentDelay": "Torrent Delay",
|
||||
"TorrentDelay": "Opóźnienie Torrenta",
|
||||
"TorrentDelayHelpText": "Opóźnienie w ciągu kilku minut, aby poczekać przed złapaniem torrenta",
|
||||
"Torrents": "Torrenty",
|
||||
"TorrentsDisabled": "Torrenty wyłączone",
|
||||
@@ -946,7 +946,7 @@
|
||||
"Type": "Rodzaj",
|
||||
"UI": "UI",
|
||||
"UILanguage": "Język interfejsu użytkownika",
|
||||
"UILanguageHelpText": "Język, którego Radarr będzie używać w interfejsie użytkownika",
|
||||
"UILanguageHelpText": "Język, interfejsu użytkownika używanego przez Radarr",
|
||||
"UILanguageHelpTextWarning": "Wymagane przeładowanie przeglądarki",
|
||||
"UISettings": "Ustawienia interfejsu użytkownika",
|
||||
"UISettingsSummary": "Opcje z osłabionym kalendarzem, datą i kolorem",
|
||||
@@ -989,7 +989,7 @@
|
||||
"UnsavedChanges": "Niezapisane zmiany",
|
||||
"UnselectAll": "Odznacz wszystko",
|
||||
"UpdateAll": "Aktualizuj wszystko",
|
||||
"UpperCase": "Duże litery",
|
||||
"UpperCase": "Z dużej litery",
|
||||
"UpdateCheckUINotWritableMessage": "Nie można zainstalować aktualizacji, ponieważ użytkownik „{1}” nie ma prawa zapisu w folderze interfejsu użytkownika „{0}”.",
|
||||
"UpdateMechanismHelpText": "Użyj wbudowanego aktualizatora Radarr lub skryptu",
|
||||
"UpdateScriptPathHelpText": "Ścieżka do niestandardowego skryptu, który pobiera wyodrębniony pakiet aktualizacji i obsługuje pozostałą część procesu aktualizacji",
|
||||
@@ -1006,7 +1006,7 @@
|
||||
"VisitGithubCustomFormatsAphrodite": "Odwiedź wiki, aby uzyskać więcej informacji: ",
|
||||
"WaitingToProcess": "Czekam na przetworzenie",
|
||||
"Wanted": "Chciał",
|
||||
"Warn": "Ostrzec",
|
||||
"Warn": "Ostrzeż",
|
||||
"Week": "Tydzień",
|
||||
"WeekColumnHeader": "Nagłówek kolumny tygodnia",
|
||||
"Weeks": "Tygodni",
|
||||
@@ -1043,25 +1043,25 @@
|
||||
"Download": "Ściągnij",
|
||||
"DownloadClientCheckDownloadingToRoot": "Klient pobierania {0} umieszcza pliki do pobrania w folderze głównym {1}. Nie należy pobierać do folderu głównego.",
|
||||
"DeleteFileLabel": "Usuń {0} pliki filmowe",
|
||||
"UnableToAddRootFolder": "Nie można załadować folderów głównych",
|
||||
"UnableToAddRootFolder": "Nie można dodać folderu głównego",
|
||||
"AreYouSureYouWantToRemoveTheSelectedItemsFromBlocklist": "Czy na pewno chcesz usunąć wybrane elementy z czarnej listy?",
|
||||
"Blocklist": "Czarna lista",
|
||||
"BlocklistRelease": "Wydanie czarnej listy",
|
||||
"RemoveFromBlocklist": "Usuń z czarnej listy",
|
||||
"UnableToLoadBlocklist": "Nie można załadować czarnej listy",
|
||||
"UnableToLoadBlocklist": "Nie można załadować listy blokowania",
|
||||
"Blocklisted": "Czarna lista",
|
||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Czy na pewno chcesz usunąć {0} element {1} z kolejki?",
|
||||
"BlocklistReleases": "Wydanie czarnej listy",
|
||||
"Filters": "Filtr",
|
||||
"Filters": "Filtry",
|
||||
"LocalPath": "Ścieżka lokalna",
|
||||
"List": "Listy",
|
||||
"Rating": "Oceny",
|
||||
"RemotePath": "Zdalna ścieżka",
|
||||
"List": "Lista",
|
||||
"Rating": "Ocena",
|
||||
"RemotePath": "Ścieżka zdalna",
|
||||
"SelectLanguages": "Wybierz język",
|
||||
"AllCollectionsHiddenDueToFilter": "Wszystkie kolekcje są ukryte ze względu na zastosowany filtr.",
|
||||
"Collections": "Kolekcja",
|
||||
"Collections": "Kolekcje",
|
||||
"RssSyncHelpText": "Odstęp w minutach. Ustaw na zero, aby wyłączyć (zatrzyma to wszystkie automatyczne przechwytywanie zwolnień)",
|
||||
"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",
|
||||
"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.",
|
||||
@@ -1078,7 +1078,7 @@
|
||||
"ManualImportSetReleaseGroup": "Import ręczny - podaj nazwę grupy",
|
||||
"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.",
|
||||
"RemotePathMappingCheckGenericPermissions": "Klient pobierania {0} umieszcza pobrane pliki w {1}, lecz Radarr nie widzi tego folderu. Być może należy zmienić ustawienia uprawnień dostępu.",
|
||||
"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.",
|
||||
"RemoveFailed": "Usuń nieudane",
|
||||
"RemoveDownloadsAlert": "Ustawienia usuwania zostały przeniesione do ustawień poszczególnych klientów pobierania powyżej.",
|
||||
"ShowCollectionDetails": "Pokaż stan kolekcji",
|
||||
@@ -1092,7 +1092,7 @@
|
||||
"EditCollection": "Edytuj kolekcję",
|
||||
"From": "z",
|
||||
"IndexerTagHelpText": "Korzystaj z tego indeksera wyłącznie w przypadku filmów z co najmniej jednym pasującym tagiem. Pozostaw pole puste, by używać do wszystkich filmów.",
|
||||
"Letterboxd": "Letterboxd",
|
||||
"Letterboxd": "Z Letterboxd",
|
||||
"MonitorCollection": "Monitoruj kolekcję",
|
||||
"MovieAndCollection": "Film i kolekcja",
|
||||
"MovieCollectionMissingRoot": "Brak katalogu głównego dla kolekcji filmów: {0}",
|
||||
@@ -1134,5 +1134,12 @@
|
||||
"TaskUserAgentTooltip": "User-Agent podawany przez aplikację wywołującą API",
|
||||
"SetReleaseGroup": "Ustaw grupę wydającą",
|
||||
"SearchOnAddCollectionHelpText": "Po dodaniu do biblioteki wyszukaj filmy z tej kolekcji",
|
||||
"Database": "Baza danych"
|
||||
"Database": "Baza danych",
|
||||
"OnMovieAddedHelpText": "Przy dodaniu filmu",
|
||||
"ShowPosters": "Pokaż plakaty",
|
||||
"OnMovieAdded": "Przy dodaniu filmu",
|
||||
"CollectionShowPostersHelpText": "Pokaż plakaty elementów kolekcji",
|
||||
"CollectionOptions": "Opcje Kolekcji",
|
||||
"CollectionShowDetailsHelpText": "Pokaż status i właściwości kolekcji",
|
||||
"CollectionShowOverviewsHelpText": "Pokaż przegląd kolekcji"
|
||||
}
|
||||
|
||||
@@ -1116,5 +1116,12 @@
|
||||
"Collections": "Coleção",
|
||||
"RssSyncHelpText": "Intervalo em minutos. Defina como zero para desativar (isso parará toda a captura automática)",
|
||||
"MonitorMovies": "Monitorar filme",
|
||||
"NoCollections": "Nenhum filme encontrado. Para começar, adiciona um novo filme ou importa alguns já existentes."
|
||||
"NoCollections": "Nenhum filme encontrado. Para começar, adiciona um novo filme ou importa alguns já existentes.",
|
||||
"MovieAndCollection": "Filme e Coleção",
|
||||
"CollectionsSelectedInterp": "{0} Coleções Selecionadas",
|
||||
"EditCollection": "Editar Coleção",
|
||||
"ChooseImportMode": "Selecionar Modo de Importação",
|
||||
"InstanceName": "Nome da Instancia",
|
||||
"CollectionOptions": "Opções de Coleção",
|
||||
"CollectionShowDetailsHelpText": "Mostrar estado da coleção e proprieades"
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"IndexerStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a falhas",
|
||||
"IndexersSettingsSummary": "Indexadores e restrições de lançamento",
|
||||
"IndexerSettings": "Configurações do indexador",
|
||||
"IndexerSearchCheckNoInteractiveMessage": "Sem indexadores disponíveis com a Pesquisa Interativa habilitada, o Radarr não fornecerá resultados interativos de pesquisa",
|
||||
"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",
|
||||
"IndexerSearchCheckNoAutomaticMessage": "Nenhum indexador disponível com a Pesquisa automática habilitada, o Radarr não fornecerá nenhum resultado de pesquisa automática",
|
||||
"Indexers": "Indexadores",
|
||||
@@ -668,7 +668,7 @@
|
||||
"SomeResultsHiddenFilter": "Alguns resultados estão ocultos pelo filtro aplicado",
|
||||
"Socks5": "Socks5 (suporte ao TOR)",
|
||||
"Small": "Pequeno",
|
||||
"SkipFreeSpaceCheckWhenImportingHelpText": "Usar quando o Radarr não conseguir detectar espaço livre da pasta raiz do filme",
|
||||
"SkipFreeSpaceCheckWhenImportingHelpText": "Usar o Radarr quando for impossível detectar o espaço livre da sua pasta raiz do filme",
|
||||
"SkipFreeSpaceCheck": "Ignorar verificação de espaço livre",
|
||||
"SizeOnDisk": "Tamanho em disco",
|
||||
"Size": "Tamanho",
|
||||
@@ -961,7 +961,7 @@
|
||||
"UpdateSelected": "Atualizar selecionado(s)",
|
||||
"UpdateScriptPathHelpText": "Caminho para um script personalizado que usa um pacote de atualização extraído e lida com o restante do processo de atualização",
|
||||
"Updates": "Atualizações",
|
||||
"UpdateMechanismHelpText": "Usar o atualizador embutido do Radarr ou um script",
|
||||
"UpdateMechanismHelpText": "Usar atualizador integrado do Radarr ou um script",
|
||||
"UpdateCheckUINotWritableMessage": "Não é possível instalar a atualização porque a pasta de interface do usuário '{0}' não é gravável pelo usuário '{1}'.",
|
||||
"UpdateCheckStartupTranslocationMessage": "Não é possível instalar a atualização porque a pasta de inicialização '{0}' está em uma pasta App Translocation.",
|
||||
"UpdateCheckStartupNotWritableMessage": "Não é possível instalar a atualização porque a pasta de inicialização '{0}' não pode ser gravada pelo usuário '{1}'.",
|
||||
@@ -1053,7 +1053,7 @@
|
||||
"RemotePathMappingCheckImportFailed": "O Radarr não conseguiu importar um filme. Verifique os logs para saber mais.",
|
||||
"RemotePathMappingCheckFileRemoved": "O arquivo {0} foi removido no meio do processamento.",
|
||||
"RemotePathMappingCheckDownloadPermissions": "O Radarr pode ver, mas não pode acessar o filme baixado {0}. Provável erro de permissões.",
|
||||
"RemotePathMappingCheckGenericPermissions": "Cliente para download {0} coloca downloads em {1}, mas o Radarr não pode ver este diretório. Você pode precisar ajustar as permissões da pasta.",
|
||||
"RemotePathMappingCheckGenericPermissions": "O cliente para download {0} põe os downloads em {1}, mas o Radarr não pode ver esse diretório. Você pode precisar ajustar as permissões da pasta.",
|
||||
"RemotePathMappingCheckWrongOSPath": "O cliente de download remoto {0} coloca downloads em {1}, mas este não é um caminho {2} válido. Revise seus mapeamentos de caminho remoto e baixe as configurações do cliente.",
|
||||
"RemotePathMappingCheckLocalWrongOSPath": "O cliente de download local {0} coloca downloads em {1}, mas este não é um caminho {2} válido. Revise as configurações do seu cliente de download.",
|
||||
"RemotePathMappingCheckLocalFolderMissing": "O cliente de download remoto {0} coloca downloads em {1}, mas esse diretório parece não existir. Mapeamento de caminho remoto provavelmente ausente ou incorreto.",
|
||||
@@ -1119,7 +1119,7 @@
|
||||
"AllCollectionsHiddenDueToFilter": "Todos os filmes estão ocultos devido ao filtro aplicado.",
|
||||
"Collections": "Coleção",
|
||||
"MonitorMovies": "Monitorar filme",
|
||||
"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",
|
||||
"UnableToLoadCollections": "Não foi possível carregar as coleções",
|
||||
"ChooseImportMode": "Escolher o Modo de Importação",
|
||||
@@ -1141,5 +1141,6 @@
|
||||
"CollectionOptions": "Opções de Coleção",
|
||||
"CollectionShowDetailsHelpText": "Mostrar estado e propriedades 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"
|
||||
}
|
||||
|
||||
@@ -110,5 +110,6 @@
|
||||
"DeleteNotificationMessageText": "Naozaj chcete zmazať značku formátu {0} ?",
|
||||
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Naozaj chcete odobrať {0} položku {1} z fronty?",
|
||||
"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."
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"About": "關於",
|
||||
"Add": "添加"
|
||||
"Add": "添加",
|
||||
"AcceptConfirmationModal": "接受確認模式",
|
||||
"Actions": "行動",
|
||||
"Activity": "活動"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
@@ -19,13 +18,13 @@ namespace NzbDrone.Core.MediaCover
|
||||
public class MediaCoverProxy : IMediaCoverProxy
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly ICached<string> _cache;
|
||||
|
||||
public MediaCoverProxy(IHttpClient httpClient, IOptionsMonitor<ConfigFileOptions> configFileOptions, ICacheManager cacheManager)
|
||||
public MediaCoverProxy(IHttpClient httpClient, IConfigFileProvider configFileProvider, ICacheManager cacheManager)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_configFileOptions = configFileOptions;
|
||||
_configFileProvider = configFileProvider;
|
||||
_cache = cacheManager.GetCache<string>(GetType());
|
||||
}
|
||||
|
||||
@@ -38,7 +37,7 @@ namespace NzbDrone.Core.MediaCover
|
||||
_cache.ClearExpired();
|
||||
|
||||
var fileName = Path.GetFileName(url);
|
||||
return _configFileOptions.CurrentValue.UrlBase + @"/MediaCoverProxy/" + hash + "/" + fileName;
|
||||
return _configFileProvider.UrlBase + @"/MediaCoverProxy/" + hash + "/" + fileName;
|
||||
}
|
||||
|
||||
public string GetUrl(string hash)
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Disk;
|
||||
@@ -36,7 +35,7 @@ namespace NzbDrone.Core.MediaCover
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly ICoverExistsSpecification _coverExistsSpecification;
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@@ -52,7 +51,7 @@ namespace NzbDrone.Core.MediaCover
|
||||
IDiskProvider diskProvider,
|
||||
IAppFolderInfo appFolderInfo,
|
||||
ICoverExistsSpecification coverExistsSpecification,
|
||||
IOptionsMonitor<ConfigFileOptions> configFileOptions,
|
||||
IConfigFileProvider configFileProvider,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
@@ -61,7 +60,7 @@ namespace NzbDrone.Core.MediaCover
|
||||
_httpClient = httpClient;
|
||||
_diskProvider = diskProvider;
|
||||
_coverExistsSpecification = coverExistsSpecification;
|
||||
_configFileOptions = configFileOptions;
|
||||
_configFileProvider = configFileProvider;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
|
||||
@@ -105,7 +104,7 @@ namespace NzbDrone.Core.MediaCover
|
||||
var filePath = GetCoverPath(movieId, mediaCover.CoverType);
|
||||
|
||||
mediaCover.RemoteUrl = mediaCover.Url;
|
||||
mediaCover.Url = _configFileOptions.CurrentValue.UrlBase + @"/MediaCover/" + movieId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg";
|
||||
mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + movieId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg";
|
||||
|
||||
FileInfo file;
|
||||
var fileExists = false;
|
||||
|
||||
@@ -5,7 +5,7 @@ using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Events
|
||||
{
|
||||
public class MovieImportedEvent : IEvent
|
||||
public class MovieFileImportedEvent : IEvent
|
||||
{
|
||||
public LocalMovie MovieInfo { get; private set; }
|
||||
public MovieFile ImportedMovie { get; private set; }
|
||||
@@ -14,7 +14,7 @@ namespace NzbDrone.Core.MediaFiles.Events
|
||||
public DownloadClientItemClientInfo DownloadClientInfo { get; set; }
|
||||
public string DownloadId { get; private set; }
|
||||
|
||||
public MovieImportedEvent(LocalMovie movieInfo, MovieFile importedMovie, List<MovieFile> oldFiles, bool newDownload, DownloadClientItem downloadClientItem)
|
||||
public MovieFileImportedEvent(LocalMovie movieInfo, MovieFile importedMovie, List<MovieFile> oldFiles, bool newDownload, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
MovieInfo = movieInfo;
|
||||
ImportedMovie = importedMovie;
|
||||
@@ -153,10 +153,10 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||
return "WMA";
|
||||
}
|
||||
|
||||
Logger.Debug()
|
||||
Logger.ForDebugEvent()
|
||||
.Message("Unknown audio format: '{0}' in '{1}'.", mediaInfo.RawStreamData, sceneName)
|
||||
.WriteSentryWarn("UnknownAudioFormatFFProbe", mediaInfo.ContainerFormat, mediaInfo.AudioFormat, audioCodecID)
|
||||
.Write();
|
||||
.Log();
|
||||
|
||||
return mediaInfo.AudioFormat;
|
||||
}
|
||||
@@ -262,10 +262,10 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||
return "";
|
||||
}
|
||||
|
||||
Logger.Debug()
|
||||
Logger.ForDebugEvent()
|
||||
.Message("Unknown video format: '{0}' in '{1}'.", mediaInfo.RawStreamData, sceneName)
|
||||
.WriteSentryWarn("UnknownVideoFormatFFProbe", mediaInfo.ContainerFormat, videoFormat, videoCodecID)
|
||||
.Write();
|
||||
.Log();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
_extraService.ImportMovie(localMovie, movieFile, copyOnly);
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(new MovieImportedEvent(localMovie, movieFile, oldFiles, newDownload, downloadClientItem));
|
||||
_eventAggregator.PublishEvent(new MovieFileImportedEvent(localMovie, movieFile, oldFiles, newDownload, downloadClientItem));
|
||||
}
|
||||
catch (RootFolderNotFoundException e)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Movies.Events;
|
||||
|
||||
@@ -13,6 +14,7 @@ namespace NzbDrone.Core.Movies.Collections
|
||||
IEnumerable<MovieCollection> GetCollections(IEnumerable<int> ids);
|
||||
List<MovieCollection> GetAllCollections();
|
||||
MovieCollection UpdateCollection(MovieCollection collection);
|
||||
List<MovieCollection> UpdateCollections(List<MovieCollection> collections);
|
||||
void RemoveCollection(MovieCollection collection);
|
||||
bool Upsert(MovieCollection collection);
|
||||
bool UpsertMany(List<MovieCollection> collections);
|
||||
@@ -23,12 +25,14 @@ namespace NzbDrone.Core.Movies.Collections
|
||||
private readonly IMovieCollectionRepository _repo;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MovieCollectionService(IMovieCollectionRepository repo, IMovieService movieService, IEventAggregator eventAggregator)
|
||||
public MovieCollectionService(IMovieCollectionRepository repo, IMovieService movieService, IEventAggregator eventAggregator, Logger logger)
|
||||
{
|
||||
_repo = repo;
|
||||
_movieService = movieService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public MovieCollection AddCollection(MovieCollection newCollection)
|
||||
@@ -73,6 +77,21 @@ namespace NzbDrone.Core.Movies.Collections
|
||||
return updatedCollection;
|
||||
}
|
||||
|
||||
public List<MovieCollection> UpdateCollections(List<MovieCollection> collections)
|
||||
{
|
||||
_logger.Debug("Updating {0} movie collections", collections.Count);
|
||||
|
||||
foreach (var c in collections)
|
||||
{
|
||||
_logger.Trace("Updating: {0}", c.Title);
|
||||
}
|
||||
|
||||
_repo.UpdateMany(collections);
|
||||
_logger.Debug("{0} movie collections updated", collections.Count);
|
||||
|
||||
return collections;
|
||||
}
|
||||
|
||||
public void RemoveCollection(MovieCollection collection)
|
||||
{
|
||||
_repo.Delete(collection);
|
||||
|
||||
@@ -5,11 +5,11 @@ namespace NzbDrone.Core.Movies.Events
|
||||
{
|
||||
public class MoviesImportedEvent : IEvent
|
||||
{
|
||||
public List<int> MovieIds { get; private set; }
|
||||
public List<Movie> Movies { get; private set; }
|
||||
|
||||
public MoviesImportedEvent(List<int> movieIds)
|
||||
public MoviesImportedEvent(List<Movie> movies)
|
||||
{
|
||||
MovieIds = movieIds;
|
||||
Movies = movies;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace NzbDrone.Core.Movies
|
||||
|
||||
public void Handle(MoviesImportedEvent message)
|
||||
{
|
||||
_commandQueueManager.PushMany(message.MovieIds.Select(s => new RefreshMovieCommand(new List<int> { s }, true)).ToList());
|
||||
_commandQueueManager.PushMany(message.Movies.Select(s => new RefreshMovieCommand(new List<int> { s.Id }, true)).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace NzbDrone.Core.Movies
|
||||
{
|
||||
_movieRepository.InsertMany(newMovies);
|
||||
|
||||
_eventAggregator.PublishEvent(new MoviesImportedEvent(newMovies.Select(s => s.Id).ToList()));
|
||||
_eventAggregator.PublishEvent(new MoviesImportedEvent(newMovies));
|
||||
|
||||
return newMovies;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ using NzbDrone.Core.Movies.Events;
|
||||
|
||||
namespace NzbDrone.Core.Movies
|
||||
{
|
||||
public class RefreshCollectionService : IExecute<RefreshCollectionsCommand>, IHandle<CollectionEditedEvent>
|
||||
public class RefreshCollectionService : IExecute<RefreshCollectionsCommand>
|
||||
{
|
||||
private readonly IProvideMovieInfo _movieInfo;
|
||||
private readonly IMovieCollectionService _collectionService;
|
||||
@@ -99,21 +99,25 @@ namespace NzbDrone.Core.Movies
|
||||
{
|
||||
var existingMovies = _movieService.AllMovieTmdbIds();
|
||||
var collectionMovies = _movieMetadataService.GetMoviesByCollectionTmdbId(collection.TmdbId);
|
||||
var moviesToAdd = collectionMovies.Where(m => !existingMovies.Contains(m.TmdbId));
|
||||
|
||||
_addMovieService.AddMovies(collectionMovies.Where(m => !existingMovies.Contains(m.TmdbId)).Select(m => new Movie
|
||||
if (moviesToAdd.Any())
|
||||
{
|
||||
TmdbId = m.TmdbId,
|
||||
Title = m.Title,
|
||||
ProfileId = collection.QualityProfileId,
|
||||
RootFolderPath = collection.RootFolderPath,
|
||||
MinimumAvailability = collection.MinimumAvailability,
|
||||
AddOptions = new AddMovieOptions
|
||||
_addMovieService.AddMovies(moviesToAdd.Select(m => new Movie
|
||||
{
|
||||
SearchForMovie = collection.SearchOnAdd,
|
||||
AddMethod = AddMovieMethod.Collection
|
||||
},
|
||||
Monitored = true
|
||||
}).ToList());
|
||||
TmdbId = m.TmdbId,
|
||||
Title = m.Title,
|
||||
ProfileId = collection.QualityProfileId,
|
||||
RootFolderPath = collection.RootFolderPath,
|
||||
MinimumAvailability = collection.MinimumAvailability,
|
||||
AddOptions = new AddMovieOptions
|
||||
{
|
||||
SearchForMovie = collection.SearchOnAdd,
|
||||
AddMethod = AddMovieMethod.Collection
|
||||
},
|
||||
Monitored = true
|
||||
}).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,10 +155,5 @@ namespace NzbDrone.Core.Movies
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(CollectionEditedEvent message)
|
||||
{
|
||||
SyncCollectionMovies(message.Collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.HealthCheck;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Notifiarr
|
||||
@@ -77,6 +78,15 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
||||
variables.Add("Radarr_MovieFile_SceneName", movieFile.SceneName ?? string.Empty);
|
||||
variables.Add("Radarr_MovieFile_SourcePath", sourcePath);
|
||||
variables.Add("Radarr_MovieFile_SourceFolder", Path.GetDirectoryName(sourcePath));
|
||||
variables.Add("Radarr_MovieFile_MediaInfo_AudioChannels", MediaInfoFormatter.FormatAudioChannels(movieFile.MediaInfo).ToString());
|
||||
variables.Add("Radarr_MovieFile_MediaInfo_AudioCodec", MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, null));
|
||||
variables.Add("Radarr_MovieFile_MediaInfo_AudioLanguages", movieFile.MediaInfo.AudioLanguages.Distinct().ConcatToString(" / "));
|
||||
variables.Add("Radarr_MovieFile_MediaInfo_Languages", movieFile.MediaInfo.AudioLanguages.ConcatToString(" / "));
|
||||
variables.Add("Radarr_MovieFile_MediaInfo_Height", movieFile.MediaInfo.Height.ToString());
|
||||
variables.Add("Radarr_MovieFile_MediaInfo_Width", movieFile.MediaInfo.Width.ToString());
|
||||
variables.Add("Radarr_MovieFile_MediaInfo_Subtitles", movieFile.MediaInfo.Subtitles.ConcatToString(" / "));
|
||||
variables.Add("Radarr_MovieFile_MediaInfo_VideoCodec", MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, null));
|
||||
variables.Add("Radarr_MovieFile_MediaInfo_VideoDynamicRangeType", MediaInfoFormatter.FormatVideoDynamicRangeType(movieFile.MediaInfo));
|
||||
variables.Add("Radarr_Download_Id", message.DownloadId ?? string.Empty);
|
||||
variables.Add("Radarr_Download_Client", message.DownloadClientInfo?.Name ?? string.Empty);
|
||||
variables.Add("Radarr_Download_Client_Type", message.DownloadClientInfo?.Type ?? string.Empty);
|
||||
|
||||
@@ -78,8 +78,7 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
||||
try
|
||||
{
|
||||
var url = settings.Environment == (int)NotifiarrEnvironment.Development ? "https://dev.notifiarr.com" : "https://notifiarr.com";
|
||||
var requestBuilder = new HttpRequestBuilder(url + "/notifier.php").Post();
|
||||
requestBuilder.AddFormParameter("api", settings.APIKey).Build();
|
||||
var requestBuilder = new HttpRequestBuilder(url + "/api/v1/notification/radarr/" + settings.APIKey).Post();
|
||||
requestBuilder.AddFormParameter("instanceName", settings.InstanceName).Build();
|
||||
|
||||
foreach (string key in message.Keys)
|
||||
|
||||
@@ -17,9 +17,10 @@ namespace NzbDrone.Core.Notifications
|
||||
public class NotificationService
|
||||
: IHandle<MovieRenamedEvent>,
|
||||
IHandle<MovieGrabbedEvent>,
|
||||
IHandle<MovieImportedEvent>,
|
||||
IHandle<MovieFileImportedEvent>,
|
||||
IHandle<MoviesDeletedEvent>,
|
||||
IHandle<MovieAddedEvent>,
|
||||
IHandle<MoviesImportedEvent>,
|
||||
IHandle<MovieFileDeletedEvent>,
|
||||
IHandle<HealthCheckFailedEvent>,
|
||||
IHandle<UpdateInstalledEvent>,
|
||||
@@ -119,7 +120,7 @@ namespace NzbDrone.Core.Notifications
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(MovieImportedEvent message)
|
||||
public void Handle(MovieFileImportedEvent message)
|
||||
{
|
||||
if (!message.NewDownload)
|
||||
{
|
||||
@@ -174,6 +175,27 @@ namespace NzbDrone.Core.Notifications
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(MoviesImportedEvent message)
|
||||
{
|
||||
foreach (var notification in _notificationFactory.OnMovieAddedEnabled())
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var movie in message.Movies)
|
||||
{
|
||||
if (ShouldHandleMovie(notification.Definition, movie))
|
||||
{
|
||||
notification.OnMovieAdded(movie);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Unable to send OnMovieAdded notification to: " + notification.Definition.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(MovieRenamedEvent message)
|
||||
{
|
||||
foreach (var notification in _notificationFactory.OnRenameEnabled())
|
||||
|
||||
@@ -20,6 +20,18 @@ namespace NzbDrone.Core.Parser
|
||||
|
||||
private static readonly Regex[] ReportMovieTitleRegex = new[]
|
||||
{
|
||||
//Anime [Subgroup] and Year
|
||||
new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(1(8|9)|20)\d{2}(?!p|i|x|\d+|\]|\W\d+)))+.*?(?<hash>\[\w{8}\])?(?:$|\.)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
//Anime [Subgroup] no year, versioned title, hash
|
||||
new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)(?<title>(?![(\[]).+?)((v)(?:\d{1,2})(?:([-_. ])))(\[.*)?(?:[\[(][^])])?.*?(?<hash>\[\w{8}\])(?:$|\.)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
//Anime [Subgroup] no year, info in double sets of brackets, hash
|
||||
new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)(?<title>(?![(\[]).+?)(\[.*).*?(?<hash>\[\w{8}\])(?:$|\.)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
//Anime [Subgroup] no year, info in parentheses or brackets, hash
|
||||
new Regex(@"^(?:\[(?<subgroup>.+?)\][-_. ]?)(?<title>(?![(\[]).+)(?:[\[(][^])]).*?(?<hash>\[\w{8}\])(?:$|\.)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
//Some german or french tracker formats (missing year, ...) (Only applies to german and TrueFrench releases) - see ParserFixture for examples and tests - french removed as it broke all movies w/ french titles
|
||||
new Regex(@"^(?<title>(?![(\[]).+?)((\W|_))(" + EditionRegex + @".{1,3})?(?:(?<!(19|20)\d{2}.*?)(German|TrueFrench))(.+?)(?=((19|20)\d{2}|$))(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+))?(\W+|_|$)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
@@ -41,7 +53,7 @@ namespace NzbDrone.Core.Parser
|
||||
new Regex(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)!]))*(?<year>(1(8|9)|20)\d{2}(?!p|i|\d+|\W\d+)))+(\W+|_|$)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
//As a last resort for movies that have ( or [ in their title.
|
||||
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(1(8|9)|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(1(8|9)|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled)
|
||||
};
|
||||
|
||||
private static readonly Regex[] ReportMovieTitleFolderRegex = new[]
|
||||
@@ -97,7 +109,7 @@ namespace NzbDrone.Core.Parser
|
||||
private static readonly Regex ReportImdbId = new Regex(@"(?<imdbid>tt\d{7,8})", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
private static readonly Regex ReportTmdbId = new Regex(@"tmdb(id)?-(?<tmdbid>\d+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly RegexReplace SimpleTitleRegex = new RegexReplace(@"(?:(480|540|576|720|1080|2160)[ip]|[xh][\W_]?26[45]|DD\W?5\W1|[<>?*]|848x480|1280x720|1920x1080|3840x2160|4096x2160|(8|10)b(it)?|10-bit)\s*?",
|
||||
private static readonly RegexReplace SimpleTitleRegex = new RegexReplace(@"(?:(480|540|576|720|1080|2160)[ip]|[xh][\W_]?26[45]|DD\W?5\W1|[<>?*]|848x480|1280x720|1920x1080|3840x2160|4096x2160|(8|10)b(it)?|10-bit)\s*?(?![a-b0-9])",
|
||||
string.Empty,
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace NzbDrone.Core.Parser
|
||||
|
||||
private static readonly Regex SourceRegex = new Regex(@"\b(?:
|
||||
(?<bluray>M?BluRay|Blu-Ray|HD.?DVD|BD(?!$)|UHDBD|UHD2BD|BDISO|BDMux|BD25|BD50|BR.?DISK)|
|
||||
(?<webdl>WEB[-_. ]DL(?:mux)?|WEBDL|AmazonHD|iTunesHD|MaxdomeHD|NetflixU?HD|WebHD|[. ]WEB[. ](?:[xh]26[45]|DDP?5[. ]1)|[. ](?-i:WEB)$|(?:\d{3,4}0p)[-. ]WEB[-. ]|[-. ]WEB[-. ]\d{3,4}0p|\b\s\/\sWEB\s\/\s\b|(?:AMZN|NF|DP)[. -]WEB[. -])|
|
||||
(?<webdl>WEB[-_. ]DL(?:mux)?|WEBDL|AmazonHD|iTunesHD|MaxdomeHD|NetflixU?HD|WebHD|[. ]WEB[. ](?:[xh][ .]?26[45]|DDP?5[. ]1)|[. ](?-i:WEB)$|(?:\d{3,4}0p)[-. ]WEB[-. ]|[-. ]WEB[-. ]\d{3,4}0p|\b\s\/\sWEB\s\/\s\b|(?:AMZN|NF|DP)[. -]WEB[. -])|
|
||||
(?<webrip>WebRip|Web-Rip|WEBMux)|
|
||||
(?<hdtv>HDTV)|
|
||||
(?<bdrip>BDRip|BDLight)|
|
||||
|
||||
@@ -13,14 +13,13 @@
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0-preview.5.22301.12" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0-preview.5.22301.12" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
||||
<PackageReference Include="FluentMigrator.Runner" Version="3.3.2" />
|
||||
<PackageReference Include="FluentMigrator.Runner.SQLite" Version="3.3.2" />
|
||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NLog" Version="4.7.14" />
|
||||
<PackageReference Include="NLog" Version="5.0.1" />
|
||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
|
||||
<PackageReference Include="MonoTorrent" Version="2.0.5" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.4" />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
@@ -17,21 +16,19 @@ namespace NzbDrone.Core.Update
|
||||
public class UpdaterConfigProvider : IUpdaterConfigProvider, IHandle<ApplicationStartedEvent>
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileWriter _configFileWriter;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IDeploymentInfoProvider _deploymentInfoProvider;
|
||||
|
||||
public UpdaterConfigProvider(IDeploymentInfoProvider deploymentInfoProvider, IOptionsMonitor<ConfigFileOptions> configFileOptions, IConfigFileWriter configFileWriter, Logger logger)
|
||||
public UpdaterConfigProvider(IDeploymentInfoProvider deploymentInfoProvider, IConfigFileProvider configFileProvider, Logger logger)
|
||||
{
|
||||
_deploymentInfoProvider = deploymentInfoProvider;
|
||||
_configFileOptions = configFileOptions;
|
||||
_configFileWriter = configFileWriter;
|
||||
_configFileProvider = configFileProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Handle(ApplicationStartedEvent message)
|
||||
{
|
||||
var updateMechanism = _configFileOptions.CurrentValue.UpdateMechanism;
|
||||
var updateMechanism = _configFileProvider.UpdateMechanism;
|
||||
var packageUpdateMechanism = _deploymentInfoProvider.PackageUpdateMechanism;
|
||||
|
||||
var externalMechanisms = Enum.GetValues(typeof(UpdateMechanism))
|
||||
@@ -52,7 +49,7 @@ namespace NzbDrone.Core.Update
|
||||
|
||||
if (_deploymentInfoProvider.IsExternalUpdateMechanism)
|
||||
{
|
||||
var currentBranch = _configFileOptions.CurrentValue.Branch;
|
||||
var currentBranch = _configFileProvider.Branch;
|
||||
var packageBranch = _deploymentInfoProvider.PackageBranch;
|
||||
if (packageBranch.IsNotNullOrWhiteSpace() && packageBranch != currentBranch)
|
||||
{
|
||||
@@ -66,18 +63,18 @@ namespace NzbDrone.Core.Update
|
||||
{
|
||||
var config = new Dictionary<string, object>
|
||||
{
|
||||
[nameof(_configFileOptions.CurrentValue.UpdateMechanism)] = updateMechanism
|
||||
[nameof(_configFileProvider.UpdateMechanism)] = updateMechanism
|
||||
};
|
||||
_configFileWriter.SaveConfigDictionary(config);
|
||||
_configFileProvider.SaveConfigDictionary(config);
|
||||
}
|
||||
|
||||
private void ChangeBranch(string branch)
|
||||
{
|
||||
var config = new Dictionary<string, object>
|
||||
{
|
||||
[nameof(_configFileOptions.CurrentValue.Branch)] = branch
|
||||
[nameof(_configFileProvider.Branch)] = branch
|
||||
};
|
||||
_configFileWriter.SaveConfigDictionary(config);
|
||||
_configFileProvider.SaveConfigDictionary(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Disk;
|
||||
@@ -36,8 +34,7 @@ namespace NzbDrone.Core.Update
|
||||
private readonly IVerifyUpdates _updateVerifier;
|
||||
private readonly IStartupContext _startupContext;
|
||||
private readonly IDeploymentInfoProvider _deploymentInfoProvider;
|
||||
private readonly IOptionsMonitor<ConfigFileOptions> _configFileOptions;
|
||||
private readonly IConfigFileWriter _configFileWriter;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IRuntimeInfo _runtimeInfo;
|
||||
private readonly IBackupService _backupService;
|
||||
private readonly IOsInfo _osInfo;
|
||||
@@ -53,16 +50,15 @@ namespace NzbDrone.Core.Update
|
||||
IVerifyUpdates updateVerifier,
|
||||
IStartupContext startupContext,
|
||||
IDeploymentInfoProvider deploymentInfoProvider,
|
||||
IOptionsMonitor<ConfigFileOptions> configFileOptions,
|
||||
IConfigFileWriter configFileWriter,
|
||||
IConfigFileProvider configFileProvider,
|
||||
IRuntimeInfo runtimeInfo,
|
||||
IBackupService backupService,
|
||||
IOsInfo osInfo,
|
||||
Logger logger)
|
||||
{
|
||||
if (configFileOptions == null)
|
||||
if (configFileProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configFileOptions));
|
||||
throw new ArgumentNullException(nameof(configFileProvider));
|
||||
}
|
||||
|
||||
_checkUpdateService = checkUpdateService;
|
||||
@@ -76,8 +72,7 @@ namespace NzbDrone.Core.Update
|
||||
_updateVerifier = updateVerifier;
|
||||
_startupContext = startupContext;
|
||||
_deploymentInfoProvider = deploymentInfoProvider;
|
||||
_configFileOptions = configFileOptions;
|
||||
_configFileWriter = configFileWriter;
|
||||
_configFileProvider = configFileProvider;
|
||||
_runtimeInfo = runtimeInfo;
|
||||
_backupService = backupService;
|
||||
_osInfo = osInfo;
|
||||
@@ -88,7 +83,7 @@ namespace NzbDrone.Core.Update
|
||||
{
|
||||
EnsureAppDataSafety();
|
||||
|
||||
if (OsInfo.IsWindows || _configFileOptions.CurrentValue.UpdateMechanism != UpdateMechanism.Script)
|
||||
if (OsInfo.IsWindows || _configFileProvider.UpdateMechanism != UpdateMechanism.Script)
|
||||
{
|
||||
var startupFolder = _appFolderInfo.StartUpFolder;
|
||||
var uiFolder = Path.Combine(startupFolder, "UI");
|
||||
@@ -142,7 +137,7 @@ namespace NzbDrone.Core.Update
|
||||
|
||||
_backupService.Backup(BackupType.Update);
|
||||
|
||||
if (OsInfo.IsNotWindows && _configFileOptions.CurrentValue.UpdateMechanism == UpdateMechanism.Script)
|
||||
if (OsInfo.IsNotWindows && _configFileProvider.UpdateMechanism == UpdateMechanism.Script)
|
||||
{
|
||||
InstallUpdateWithScript(updateSandboxFolder);
|
||||
return true;
|
||||
@@ -175,7 +170,7 @@ namespace NzbDrone.Core.Update
|
||||
|
||||
private void EnsureValidBranch(UpdatePackage package)
|
||||
{
|
||||
var currentBranch = _configFileOptions.CurrentValue.Branch;
|
||||
var currentBranch = _configFileProvider.Branch;
|
||||
if (package.Branch != currentBranch)
|
||||
{
|
||||
try
|
||||
@@ -183,7 +178,7 @@ namespace NzbDrone.Core.Update
|
||||
_logger.Info("Branch [{0}] is being redirected to [{1}]]", currentBranch, package.Branch);
|
||||
var config = new Dictionary<string, object>();
|
||||
config["Branch"] = package.Branch;
|
||||
_configFileWriter.SaveConfigDictionary(config);
|
||||
_configFileProvider.SaveConfigDictionary(config);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -194,7 +189,7 @@ namespace NzbDrone.Core.Update
|
||||
|
||||
private void InstallUpdateWithScript(string updateSandboxFolder)
|
||||
{
|
||||
var scriptPath = _configFileOptions.CurrentValue.UpdateScriptPath;
|
||||
var scriptPath = _configFileProvider.UpdateScriptPath;
|
||||
|
||||
if (scriptPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
@@ -209,7 +204,7 @@ namespace NzbDrone.Core.Update
|
||||
_logger.Info("Removing Radarr.Update");
|
||||
_diskProvider.DeleteFolder(_appFolderInfo.GetUpdateClientFolder(), true);
|
||||
|
||||
_logger.ProgressInfo("Starting update script: {0}", _configFileOptions.CurrentValue.UpdateScriptPath);
|
||||
_logger.ProgressInfo("Starting update script: {0}", _configFileProvider.UpdateScriptPath);
|
||||
_processProvider.Start(scriptPath, GetUpdaterArgs(updateSandboxFolder));
|
||||
}
|
||||
|
||||
@@ -248,19 +243,19 @@ namespace NzbDrone.Core.Update
|
||||
return null;
|
||||
}
|
||||
|
||||
if (OsInfo.IsNotWindows && !_configFileOptions.CurrentValue.UpdateAutomatically && updateTrigger != CommandTrigger.Manual)
|
||||
if (OsInfo.IsNotWindows && !_configFileProvider.UpdateAutomatically && updateTrigger != CommandTrigger.Manual)
|
||||
{
|
||||
_logger.ProgressDebug("Auto-update not enabled, not installing available update.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Safety net, ConfigureUpdateMechanism should take care of invalid settings
|
||||
if (_configFileOptions.CurrentValue.UpdateMechanism == UpdateMechanism.BuiltIn && _deploymentInfoProvider.IsExternalUpdateMechanism)
|
||||
if (_configFileProvider.UpdateMechanism == UpdateMechanism.BuiltIn && _deploymentInfoProvider.IsExternalUpdateMechanism)
|
||||
{
|
||||
_logger.ProgressDebug("Built-In updater disabled, please use {0} to install", _deploymentInfoProvider.PackageUpdateMechanism);
|
||||
return null;
|
||||
}
|
||||
else if (_configFileOptions.CurrentValue.UpdateMechanism != UpdateMechanism.Script && _deploymentInfoProvider.IsExternalUpdateMechanism)
|
||||
else if (_configFileProvider.UpdateMechanism != UpdateMechanism.Script && _deploymentInfoProvider.IsExternalUpdateMechanism)
|
||||
{
|
||||
_logger.ProgressDebug("Update available, please use {0} to install", _deploymentInfoProvider.PackageUpdateMechanism);
|
||||
return null;
|
||||
@@ -320,8 +315,8 @@ namespace NzbDrone.Core.Update
|
||||
_logger.Debug("Post-install update check requested");
|
||||
|
||||
// Don't do a prestartup update check unless BuiltIn update is enabled
|
||||
if (!_configFileOptions.CurrentValue.UpdateAutomatically ||
|
||||
_configFileOptions.CurrentValue.UpdateMechanism != UpdateMechanism.BuiltIn ||
|
||||
if (!_configFileProvider.UpdateAutomatically ||
|
||||
_configFileProvider.UpdateMechanism != UpdateMechanism.BuiltIn ||
|
||||
_deploymentInfoProvider.IsExternalUpdateMechanism)
|
||||
{
|
||||
_logger.Debug("Built-in updater disabled, skipping post-install update check");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user