1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-14 15:46:43 -04:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Qstick
1b4a6b1309 New: Add Changes to UpdatedEvent Notifications 2022-06-04 00:02:09 -05:00
Qstick
fcebfe6759 Fixed: Duplicate changes in app updated modal
[common]
2022-06-04 00:01:56 -05:00
105 changed files with 670 additions and 1136 deletions

View File

@@ -173,6 +173,7 @@ stages:
key: 'yarn | "$(osName)" | yarn.lock'
restoreKeys: |
yarn | "$(osName)"
yarn
path: $(yarnCacheFolder)
displayName: Cache Yarn packages
- bash: ./build.sh --frontend
@@ -975,6 +976,7 @@ stages:
key: 'yarn | "$(osName)" | yarn.lock'
restoreKeys: |
yarn | "$(osName)"
yarn
path: $(yarnCacheFolder)
displayName: Cache Yarn packages
- bash: ./build.sh --lint

View File

@@ -181,13 +181,12 @@ class Blocklist extends Component {
>
<TableBody>
{
items.map((item, index) => {
items.map((item) => {
return (
<BlocklistRowConnector
key={item.id}
isSelected={selectedState[item.id] || false}
columns={columns}
index={index}
{...item}
onSelectedChange={this.onSelectedChange}
/>

View File

@@ -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}>
<TmdbRating
<HeartRating
ratings={ratings}
iconSize={13}
/>

View File

@@ -1,5 +1,4 @@
.container {
display: flex;
.movie {
padding: 10px 20px;
width: 100%;
@@ -7,19 +6,3 @@
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;
}

View File

@@ -1,8 +1,6 @@
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';
@@ -20,7 +18,6 @@ class ImportMovieSearchResult extends Component {
render() {
const {
tmdbId,
title,
year,
studio,
@@ -28,30 +25,17 @@ class ImportMovieSearchResult extends Component {
} = this.props;
return (
<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>
<Link
className={styles.movie}
onPress={this.onPress}
>
<ImportMovieTitle
title={title}
year={year}
network={studio}
isExistingMovie={isExistingMovie}
/>
</Link>
);
}
}

View File

@@ -89,12 +89,12 @@ function AppUpdatedModalContent(props) {
<UpdateChanges
title={translate('New')}
changes={update.changes.new}
changes={Array.from(new Set(update.changes.new))}
/>
<UpdateChanges
title={translate('Fixed')}
changes={update.changes.fixed}
changes={Array.from(new Set(update.changes.fixed))}
/>
</div>
}

View File

@@ -1,9 +1,6 @@
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';
@@ -25,9 +22,6 @@ class CollectionFooter extends Component {
this.state = {
monitor: NO_CHANGE,
monitored: NO_CHANGE,
qualityProfileId: NO_CHANGE,
minimumAvailability: NO_CHANGE,
rootFolderPath: NO_CHANGE,
destinationRootFolder: null
};
}
@@ -42,10 +36,7 @@ class CollectionFooter extends Component {
if (prevProps.isSaving && !isSaving && !saveError) {
this.setState({
monitored: NO_CHANGE,
monitor: NO_CHANGE,
qualityProfileId: NO_CHANGE,
rootFolderPath: NO_CHANGE,
minimumAvailability: NO_CHANGE
monitor: NO_CHANGE
});
}
@@ -64,9 +55,7 @@ class CollectionFooter extends Component {
onUpdateSelectedPress = () => {
const {
monitor,
monitored,
qualityProfileId,
minimumAvailability
monitored
} = this.state;
const changes = {};
@@ -79,14 +68,6 @@ 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);
};
@@ -101,10 +82,7 @@ class CollectionFooter extends Component {
const {
monitored,
monitor,
qualityProfileId,
minimumAvailability,
rootFolderPath
monitor
} = this.state;
const monitoredOptions = [
@@ -147,52 +125,6 @@ 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

View File

@@ -0,0 +1,5 @@
.image {
align-content: center;
margin-right: 5px;
vertical-align: -0.125em;
}

File diff suppressed because one or more lines are too long

View File

@@ -22,11 +22,11 @@ class ImdbRating extends PureComponent {
let ratingString = '0.0';
if (rating) {
ratingString = `${rating.value.toFixed(1)}`;
ratingString = `${rating.value}`;
}
return (
<span title={`${rating ? rating.votes : 0} votes`}>
<span title={`${rating.votes} votes`}>
{
!hideIcon &&
<img

View File

@@ -21,11 +21,9 @@ 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 (
@@ -34,7 +32,7 @@ class RottenTomatoRating extends PureComponent {
!hideIcon &&
<img
className={styles.image}
src={ratingImage}
src={rating.value > 50 ? rtFresh : rtRotten}
style={{
height: `${iconSize}px`
}}

View File

@@ -22,7 +22,7 @@ class TmdbRating extends PureComponent {
let ratingString = '0%';
if (rating) {
ratingString = `${(rating.value * 10).toFixed()}%`;
ratingString = `${rating.value * 10}%`;
}
return (

View File

@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import TmdbRating from 'Components/TmdbRating';
import HeartRating from 'Components/HeartRating';
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}>
<TmdbRating
<HeartRating
ratings={ratings}
/>
</div>

View File

@@ -1,5 +1,6 @@
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';
@@ -7,7 +8,6 @@ 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]}
>
<TmdbRating
<HeartRating
ratings={ratings}
/>
</VirtualTableRowCell>

View File

@@ -102,21 +102,12 @@ function MovieIndexSortMenu(props) {
</SortMenuItem>
<SortMenuItem
name="imdbRating"
name="ratings"
sortKey={sortKey}
sortDirection={sortDirection}
onPress={onSortSelect}
>
{translate('ImdbRating')}
</SortMenuItem>
<SortMenuItem
name="tmdbRating"
sortKey={sortKey}
sortDirection={sortDirection}
onPress={onSortSelect}
>
{translate('TmdbRating')}
{translate('Ratings')}
</SortMenuItem>
<SortMenuItem

View File

@@ -77,9 +77,7 @@
flex: 0 0 120px;
}
.imdbRating,
.tmdbRating,
.rottenTomatoesRating {
.ratings {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 80px;

View File

@@ -84,9 +84,7 @@
flex: 0 0 120px;
}
.imdbRating,
.tmdbRating,
.rottenTomatoesRating {
.ratings {
composes: cell;
flex: 0 0 80px;

View File

@@ -1,15 +1,13 @@
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';
@@ -351,39 +349,13 @@ class MovieIndexRow extends Component {
);
}
if (name === 'tmdbRating') {
if (name === 'ratings') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
<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
<HeartRating
ratings={ratings}
/>
</VirtualTableRowCell>

View File

@@ -15,13 +15,12 @@
.tmdbId,
.movieYear {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 80px;
flex: 0 0 70px;
}
.actions {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 70px;
display: flex;
justify-content: flex-end;
flex: 1 0 auto;
padding-right: 10px;
}

View File

@@ -1,16 +1,13 @@
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 {
@@ -58,82 +55,28 @@ class ImportListExclusion extends Component {
render() {
const {
id,
isSelected,
onSelectedChange,
columns,
movieTitle,
tmdbId,
movieYear
} = this.props;
return (
<TableRow>
<TableSelectCell
id={id}
isSelected={isSelected}
onSelectedChange={onSelectedChange}
/>
<div
className={classNames(
styles.importExclusion
)}
>
<div className={styles.tmdbId}>{tmdbId}</div>
<div className={styles.movieTitle}>{movieTitle}</div>
<div className={styles.movieYear}>{movieYear}</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;
})
}
<div className={styles.actions}>
<Link
onPress={this.onEditImportExclusionPress}
>
<Icon name={icons.EDIT} />
</Link>
</div>
<EditImportListExclusionModalConnector
id={id}
@@ -151,7 +94,7 @@ class ImportListExclusion extends Component {
onConfirm={this.onConfirmDeleteImportExclusion}
onCancel={this.onDeleteImportExclusionModalClose}
/>
</TableRow>
</div>
);
}
}
@@ -161,9 +104,6 @@ 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
};

View File

@@ -4,12 +4,8 @@ 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';
@@ -23,10 +19,6 @@ class ImportListExclusions extends Component {
super(props, context);
this.state = {
allSelected: false,
allUnselected: false,
lastToggled: null,
selectedState: {},
isAddImportExclusionModalOpen: false
};
}
@@ -34,16 +26,6 @@ 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 });
};
@@ -59,50 +41,41 @@ 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>
<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>
{
items.map((item, index) => {
return (
<ImportListExclusion
key={item.id}
{...item}
{...otherProps}
index={index}
onConfirmDeleteImportExclusion={onConfirmDeleteImportExclusion}
/>
);
})
}
</div>
<div className={styles.addImportExclusion}>
@@ -128,7 +101,6 @@ 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
};

View File

@@ -4,7 +4,6 @@ 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
@@ -49,34 +48,7 @@ export default {
items: [],
isSaving: false,
saveError: null,
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
}
]
pendingChanges: {}
},
//

View File

@@ -162,14 +162,6 @@ 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];

View File

@@ -254,32 +254,27 @@ export const actionHandlers = handleThunks({
const {
collectionIds,
monitored,
monitor,
qualityProfileId,
rootFolderPath,
minimumAvailability
monitor
} = payload;
const response = {};
const collections = [];
if (payload.hasOwnProperty('monitored')) {
response.monitored = monitored;
}
collectionIds.forEach((id) => {
const collectionToUpdate = { id };
if (payload.hasOwnProperty('monitored')) {
collectionToUpdate.monitored = monitored;
}
collections.push(collectionToUpdate);
});
if (payload.hasOwnProperty('monitor')) {
response.monitorMovies = monitor === 'monitored';
}
if (payload.hasOwnProperty('qualityProfileId')) {
response.qualityProfileId = qualityProfileId;
}
if (payload.hasOwnProperty('minimumAvailability')) {
response.minimumAvailability = minimumAvailability;
}
response.rootFolderPath = rootFolderPath;
response.collectionIds = collectionIds;
response.collections = collections;
dispatch(set({
section,

View File

@@ -178,20 +178,8 @@ export const defaultState = {
isVisible: true
},
{
name: 'tmdbRating',
label: translate('TmdbRating'),
isSortable: true,
isVisible: false
},
{
name: 'rottenTomatoesRating',
label: translate('RottenTomatoesRating'),
isSortable: true,
isVisible: false
},
{
name: 'imdbRating',
label: translate('ImdbRating'),
name: 'ratings',
label: translate('Ratings'),
isSortable: true,
isVisible: false
},
@@ -236,22 +224,10 @@ export const defaultState = {
return originalLanguage.name;
},
imdbRating: function(item) {
ratings: function(item) {
const { ratings = {} } = item;
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;
return ratings.tmdb? ratings.tmdb.value : 0;
}
},
@@ -437,11 +413,6 @@ export const defaultState = {
label: translate('ImdbRating'),
type: filterBuilderTypes.NUMBER
},
{
name: 'rottenTomatoesRating',
label: translate('RottenTomatoesRating'),
type: filterBuilderTypes.NUMBER
},
{
name: 'imdbVotes',
label: translate('ImdbVotes'),

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using NLog;
using NLog.Fluent;
@@ -10,46 +8,47 @@ namespace NzbDrone.Common.Instrumentation.Extensions
{
public static readonly Logger SentryLogger = LogManager.GetLogger("Sentry");
public static LogEventBuilder SentryFingerprint(this LogEventBuilder logBuilder, params string[] fingerprint)
public static LogBuilder SentryFingerprint(this LogBuilder logBuilder, params string[] fingerprint)
{
return logBuilder.Property("Sentry", fingerprint);
}
public static LogEventBuilder WriteSentryDebug(this LogEventBuilder logBuilder, params string[] fingerprint)
public static LogBuilder WriteSentryDebug(this LogBuilder logBuilder, params string[] fingerprint)
{
return LogSentryMessage(logBuilder, LogLevel.Debug, fingerprint);
}
public static LogEventBuilder WriteSentryInfo(this LogEventBuilder logBuilder, params string[] fingerprint)
public static LogBuilder WriteSentryInfo(this LogBuilder logBuilder, params string[] fingerprint)
{
return LogSentryMessage(logBuilder, LogLevel.Info, fingerprint);
}
public static LogEventBuilder WriteSentryWarn(this LogEventBuilder logBuilder, params string[] fingerprint)
public static LogBuilder WriteSentryWarn(this LogBuilder logBuilder, params string[] fingerprint)
{
return LogSentryMessage(logBuilder, LogLevel.Warn, fingerprint);
}
public static LogEventBuilder WriteSentryError(this LogEventBuilder logBuilder, params string[] fingerprint)
public static LogBuilder WriteSentryError(this LogBuilder logBuilder, params string[] fingerprint)
{
return LogSentryMessage(logBuilder, LogLevel.Error, fingerprint);
}
private static LogEventBuilder LogSentryMessage(LogEventBuilder logBuilder, LogLevel level, string[] fingerprint)
private static LogBuilder LogSentryMessage(LogBuilder logBuilder, LogLevel level, string[] fingerprint)
{
SentryLogger.ForLogEvent(level)
.CopyLogEvent(logBuilder.LogEvent)
SentryLogger.Log(level)
.CopyLogEvent(logBuilder.LogEventInfo)
.SentryFingerprint(fingerprint)
.Log();
.Write();
return logBuilder.Property<string>("Sentry", null);
return logBuilder.Property("Sentry", null);
}
private static LogEventBuilder CopyLogEvent(this LogEventBuilder logBuilder, LogEventInfo logEvent)
private static LogBuilder CopyLogEvent(this LogBuilder logBuilder, LogEventInfo logEvent)
{
return logBuilder.TimeStamp(logEvent.TimeStamp)
return logBuilder.LoggerName(logEvent.LoggerName)
.TimeStamp(logEvent.TimeStamp)
.Message(logEvent.Message, logEvent.Parameters)
.Properties(logEvent.Properties.Select(p => new KeyValuePair<string, object>(p.Key.ToString(), p.Value)))
.Properties(logEvent.Properties.ToDictionary(v => v.Key, v => v.Value))
.Exception(logEvent.Exception);
}
}

View File

@@ -1,16 +1,13 @@
using System;
using System.Text;
using NLog;
using NLog;
using NLog.Targets;
namespace NzbDrone.Common.Instrumentation
{
public class NzbDroneFileTarget : FileTarget
{
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
protected override string GetFormattedMessage(LogEventInfo logEvent)
{
var result = CleanseLogMessage.Cleanse(Layout.Render(logEvent));
target.Append(result);
return CleanseLogMessage.Cleanse(Layout.Render(logEvent));
}
}
}

View File

@@ -34,8 +34,6 @@ namespace NzbDrone.Common.Instrumentation
var appFolderInfo = new AppFolderInfo(startupContext);
RegisterGlobalFilters();
if (Debugger.IsAttached)
{
RegisterDebugger();
@@ -99,21 +97,10 @@ 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;

View File

@@ -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="5.0.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
<PackageReference Include="NLog" Version="4.7.14" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.4" />
<PackageReference Include="Sentry" Version="3.15.0" />
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
<PackageReference Include="NLog.Targets.Syslog" Version="6.0.3" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="System.Text.Json" Version="6.0.4" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />

View File

@@ -56,51 +56,6 @@ 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()
{

View File

@@ -176,7 +176,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
public void should_return_ok_on_movie_imported_event()
{
GivenFolderExists(_downloadRootPath);
var importEvent = new MovieFileImportedEvent(new LocalMovie(), new MovieFile(), new List<MovieFile>(), true, new DownloadClientItem());
var importEvent = new MovieImportedEvent(new LocalMovie(), new MovieFile(), new List<MovieFile>(), true, new DownloadClientItem());
Subject.Check(importEvent).ShouldBeOk();
}

View File

@@ -91,7 +91,7 @@ namespace NzbDrone.Core.Test.HistoryTests
DownloadId = "abcd"
};
Subject.Handle(new MovieFileImportedEvent(localMovie, movieFile, new List<MovieFile>(), true, downloadClientItem));
Subject.Handle(new MovieImportedEvent(localMovie, movieFile, new List<MovieFile>(), true, downloadClientItem));
Mocker.GetMock<IHistoryRepository>()
.Verify(v => v.Insert(It.Is<MovieHistory>(h => h.SourceTitle == Path.GetFileNameWithoutExtension(localMovie.Path))));

View File

@@ -26,7 +26,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
}
[Test]
public void should_delete_orphaned_collection_with_meta_but_no_movie_items()
public void should_not_delete_unorphaned_collection_items()
{
var collection = Builder<MovieCollection>.CreateNew()
.With(h => h.Id = 3)
@@ -40,27 +40,6 @@ 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);
}

View File

@@ -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<MovieFileImportedEvent>()), Times.Once());
.Verify(v => v.PublishEvent(It.IsAny<MovieImportedEvent>()), Times.Once());
}
[Test]

View File

@@ -67,35 +67,6 @@ 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)]

View File

@@ -189,7 +189,6 @@ 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);
@@ -208,7 +207,6 @@ 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);

View File

@@ -85,7 +85,7 @@ namespace NzbDrone.Core.Datastore.Migration
var moviePath = reader.GetString(3);
var data = STJson.Deserialize<MovieCollection207>(collection);
if (data.TmdbId == 0 || newCollections.Any(d => d.TmdbId == data.TmdbId))
if (newCollections.Any(d => d.TmdbId == data.TmdbId))
{
continue;
}
@@ -104,14 +104,12 @@ namespace NzbDrone.Core.Datastore.Migration
rootFolderPath = moviePath.GetParentPath();
}
var collectionName = data.Name ?? $"Collection {data.TmdbId}";
newCollections.Add(new MovieCollection208
{
TmdbId = data.TmdbId,
Title = collectionName,
CleanTitle = collectionName.CleanMovieTitle(),
SortTitle = Parser.Parser.NormalizeTitle(collectionName),
Title = data.Name,
CleanTitle = data.Name.CleanMovieTitle(),
SortTitle = Parser.Parser.NormalizeTitle(data.Name),
Added = added,
QualityProfileId = qualityProfileId,
RootFolderPath = rootFolderPath,

View File

@@ -1,15 +0,0 @@
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");
}
}
}

View File

@@ -180,14 +180,14 @@ namespace NzbDrone.Core.Download
}
else
{
_logger.ForDebugEvent()
_logger.Debug()
.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")
.Log();
.Write();
}
trackedDownload.State = TrackedDownloadState.Imported;

View File

@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Download.History
public class DownloadHistoryService : IDownloadHistoryService,
IHandle<MovieGrabbedEvent>,
IHandle<MovieFileImportedEvent>,
IHandle<MovieImportedEvent>,
IHandle<DownloadCompletedEvent>,
IHandle<DownloadFailedEvent>,
IHandle<DownloadIgnoredEvent>,
@@ -120,7 +120,7 @@ namespace NzbDrone.Core.Download.History
_repository.Insert(history);
}
public void Handle(MovieFileImportedEvent message)
public void Handle(MovieImportedEvent message)
{
if (!message.NewDownload)
{

View File

@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
public class DownloadMonitoringService : IExecute<RefreshMonitoredDownloadsCommand>,
IExecute<CheckForFinishedDownloadCommand>,
IHandle<MovieGrabbedEvent>,
IHandle<MovieFileImportedEvent>,
IHandle<MovieImportedEvent>,
IHandle<DownloadsProcessedEvent>,
IHandle<TrackedDownloadsRemovedEvent>
{
@@ -167,7 +167,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
_refreshDebounce.Execute();
}
public void Handle(MovieFileImportedEvent message)
public void Handle(MovieImportedEvent message)
{
_refreshDebounce.Execute();
}

View File

@@ -158,32 +158,22 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
{
var setRating = new XElement("ratings");
var defaultRatingSet = false;
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);
}
if (movie.MovieMetadata.Value.Ratings.Imdb?.Votes > 0)
{
var setRateImdb = new XElement("rating", new XAttribute("name", "imdb"), new XAttribute("max", "10"), new XAttribute("default", "true"));
var setRateImdb = new XElement("rating", new XAttribute("name", "imdb"), new XAttribute("max", "10"));
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);
}

View File

@@ -10,7 +10,7 @@ using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{
[CheckOn(typeof(ProviderUpdatedEvent<IImportList>))]
[CheckOn(typeof(MovieFileImportedEvent), CheckOnCondition.FailedOnly)]
[CheckOn(typeof(MovieImportedEvent), CheckOnCondition.FailedOnly)]
[CheckOn(typeof(MovieImportFailedEvent), CheckOnCondition.SuccessfulOnly)]
public class ImportListRootFolderCheck : HealthCheckBase
{

View File

@@ -2,15 +2,13 @@ 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(ModelEvent<RootFolder>))]
[CheckOn(typeof(CollectionEditedEvent), CheckOnCondition.Always)]
public class MovieCollectionRootFolderCheck : HealthCheckBase
{
private readonly IMovieCollectionService _collectionService;

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
[CheckOn(typeof(ProviderUpdatedEvent<IDownloadClient>))]
[CheckOn(typeof(ProviderDeletedEvent<IDownloadClient>))]
[CheckOn(typeof(ModelEvent<RemotePathMapping>))]
[CheckOn(typeof(MovieFileImportedEvent), CheckOnCondition.FailedOnly)]
[CheckOn(typeof(MovieImportedEvent), CheckOnCondition.FailedOnly)]
[CheckOn(typeof(MovieImportFailedEvent), CheckOnCondition.SuccessfulOnly)]
public class RemotePathMappingCheck : HealthCheckBase, IProvideHealthCheck
{

View File

@@ -77,15 +77,12 @@ namespace NzbDrone.Core.HealthCheck
.ToDictionary(g => g.Key, g => g.ToArray());
}
private void PerformHealthCheck(IProvideHealthCheck[] healthChecks, bool performServerChecks = false)
private void PerformHealthCheck(IProvideHealthCheck[] healthChecks)
{
var results = healthChecks.Select(c => c.Check())
.ToList();
if (performServerChecks)
{
results.AddRange(_serverSideNotificationService.GetServerChecks());
}
results.AddRange(_serverSideNotificationService.GetServerChecks());
foreach (var result in results)
{
@@ -111,17 +108,17 @@ namespace NzbDrone.Core.HealthCheck
{
if (message.Trigger == CommandTrigger.Manual)
{
PerformHealthCheck(_healthChecks, true);
PerformHealthCheck(_healthChecks);
}
else
{
PerformHealthCheck(_scheduledHealthChecks, true);
PerformHealthCheck(_scheduledHealthChecks);
}
}
public void HandleAsync(ApplicationStartedEvent message)
{
PerformHealthCheck(_startupHealthChecks, true);
PerformHealthCheck(_startupHealthChecks);
}
public void HandleAsync(IEvent message)

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Cloud;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
@@ -24,37 +23,24 @@ namespace NzbDrone.Core.HealthCheck
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
private readonly Logger _logger;
private readonly ICached<List<HealthCheck>> _cache;
public ServerSideNotificationService(IHttpClient client,
IConfigFileProvider configFileProvider,
IRadarrCloudRequestBuilder cloudRequestBuilder,
ICacheManager cacheManager,
Logger logger)
public ServerSideNotificationService(IHttpClient client, IConfigFileProvider configFileProvider, IRadarrCloudRequestBuilder cloudRequestBuilder, Logger logger)
{
_client = client;
_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", _configFileProvider.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");

View File

@@ -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(MovieFileImportedEvent trackedDownload);
string FindDownloadId(MovieImportedEvent trackedDownload);
List<MovieHistory> Since(DateTime date, MovieHistoryEventType? eventType);
}
public class HistoryService : IHistoryService,
IHandle<MovieGrabbedEvent>,
IHandle<MovieFileImportedEvent>,
IHandle<MovieImportedEvent>,
IHandle<DownloadFailedEvent>,
IHandle<MovieFileDeletedEvent>,
IHandle<MovieFileRenamedEvent>,
@@ -97,7 +97,7 @@ namespace NzbDrone.Core.History
_historyRepository.UpdateMany(toUpdate);
}
public string FindDownloadId(MovieFileImportedEvent trackedDownload)
public string FindDownloadId(MovieImportedEvent 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(MovieFileImportedEvent message)
public void Handle(MovieImportedEvent message)
{
if (!message.NewDownload)
{

View File

@@ -16,10 +16,12 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
{
using (var mapper = _database.OpenConnection())
{
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)");
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)");
}
}
}

View File

@@ -90,7 +90,7 @@ namespace NzbDrone.Core.ImportLists.Trakt
[FieldDefinition(2, Label = "Certification", HelpText = "Filter movies by a certification (NR,G,PG,PG-13,R,NC-17), (Comma Separated)")]
public string Certification { get; set; }
[FieldDefinition(3, Label = "Genres", HelpText = "Filter movies by Trakt Genre Slug (Comma Separated) Only for Popular Lists")]
[FieldDefinition(3, Label = "Genres", HelpText = "Filter movies by Trakt Genre Slug (Comma Separated)")]
public string Genres { get; set; }
[FieldDefinition(4, Label = "Years", HelpText = "Filter movies by year or year range")]

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NLog.Config;
@@ -117,6 +117,7 @@ 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 = _configFileProvider.InstanceName;

View File

@@ -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 aktivierter interaktiver Suche verfügbar, Radarr liefert keine interaktiven Suchergebnisse",
"IndexerSearchCheckNoInteractiveMessage": "Keine Indexer mit interaktiver Suche aktiviert, 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": "Sollen von dieser Liste hinzugefügte Filme oder Sammlungen als beobachtet hinzugefügt werden",
"ShouldMonitorHelpText": "Beobachte Filme die von dieser Liste hinzugefügt wurden",
"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": "Nach Filmen auf dieser Liste suchen, wenn sie der Bibliothek hinzugefügt werden",
"SearchOnAddHelpText": "Suche nach den Filmen auf der Liste nach dem hinzufügen",
"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,30 +1116,5 @@
"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 )",
"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"
"RssSyncHelpText": "Intervall in Minuten. Zum deaktivieren auf 0 setzen ( Dies wird das automatische Release erfassen deaktivieren )"
}

View File

@@ -859,7 +859,6 @@
"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",

View File

@@ -1115,9 +1115,5 @@
"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.",
"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"
"NoCollections": "No se encontraron películas, para comenzar, querrá agregar una nueva película o importar algunas existentes."
}

View File

@@ -425,7 +425,7 @@
"RestartRequiredHelpTextWarning": "Käyttöönotto vaatii uudelleenkäynnistyksen.",
"Restore": "Palauta",
"RestoreBackup": "Palauta varmuuskopio",
"RootFolder": "Juurikansio",
"RootFolder": "Pääkansio",
"RootFolderCheckMultipleMessage": "Useita juurikansioita puuttuu: {0}",
"SendAnonymousUsageData": "Lähetä nimettömiä käyttötietoja",
"Search": "Haku",
@@ -807,7 +807,7 @@
"RetentionHelpText": "Vain Usenet: Aseta nollaan asettamaan rajoittamaton säilytys",
"RetryingDownloadInterp": "Yritetään ladata uudelleen {0} osoitteessa {1}",
"RootFolderCheckSingleMessage": "Puuttuva juurikansio: {0}",
"RootFolders": "Juurikansiot",
"RootFolders": "Pääkansiot",
"RSS": "RSS",
"RSSIsNotSupportedWithThisIndexer": "RSS-syötettä ei ole käytettävissä tälle tietolähteelle",
"RSSSyncInterval": "RSS-synkronointiväli",
@@ -970,7 +970,7 @@
"UnableToLoadRemotePathMappings": "Etäsijaintien kartoitusten lataus epäonnistui.",
"UnableToLoadRestrictions": "Rajoitusten lataus epäonnistui.",
"UnableToLoadResultsIntSearch": "Elokuvahaun tuloksien lataus epäonnistui. Yritä myöhemmin uudelleen.",
"UnableToLoadRootFolders": "Juurikansioiden lataus epäonnistui.",
"UnableToLoadRootFolders": "Pääkansioiden lataus epäonnistui.",
"UnableToLoadTags": "Tunnisteiden lataus epäonnistui.",
"UnableToLoadTheCalendar": "Kalenterin lataus epäonnistui.",
"UnableToUpdateRadarrDirectly": "Radarrin suora päivitys epäonnistui.",
@@ -1041,7 +1041,7 @@
"Reddit": "Reddit",
"More": "Lisää",
"Download": "Lataa",
"DownloadClientCheckDownloadingToRoot": "Lataustyökalu '{0}' sijoittaa lataukset juurikansioon '{1}' ja näin ei pitäisi tehdä, vaan lataukset tulee tallentaa erilliseen sijaintiin.",
"DownloadClientCheckDownloadingToRoot": "Lataustyökalu '{0}' sijoittaa lataukset pääkansioon '{1}' ja näin ei pitäisi tehdä, vaan lataukset tulee tallentaa erilliseen sijaintiin.",
"DeleteFileLabel": "Poista {0} elokuvatiedosto",
"UnableToAddRootFolder": "Juurikansioiden lataus epäonnistui.",
"AreYouSureYouWantToRemoveTheSelectedItemsFromBlocklist": "Haluatko varmasti poistaa valitut kohteet estolistalta?",
@@ -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 järjestelmälokissa",
"InstanceNameHelpText": "Instanssin nimi välilehdellä ja sovelluksen Syslog-nimeksi",
"AllCollectionsHiddenDueToFilter": "Käytössä oleva suodatin on piilottanut kaikki kokoelmat.",
"Collections": "Kokoelmat",
"MonitorMovies": "Valvo elokuvia",
@@ -1134,13 +1134,5 @@
"ScrollMovies": "Vieritä elokuvia",
"ShowCollectionDetails": "Näytä kokoelman tila",
"ShowOverview": "Näytä yleiskatsaus",
"UnableToLoadCollections": "Kokoelmia ei voitu ladata",
"OnMovieAdded": "Kun elokuva lisätään",
"OnMovieAddedHelpText": "Kun elokuva lisätään",
"ShowPosters": "Näytä julisteet",
"CollectionShowOverviewsHelpText": "Näytä kokoelmien katsaukset",
"CollectionOptions": "Kokoelmien valinnat",
"CollectionShowDetailsHelpText": "Näytä kokoelmien tila ja ominaisuudet",
"CollectionShowPostersHelpText": "Näytä kokoelmien julisteet",
"RottenTomatoesRating": "Tomaattiarvio"
"UnableToLoadCollections": "Kokoelmia ei voitu ladata"
}

View File

@@ -1133,14 +1133,5 @@
"MonitorCollection": "Gyűjtemény monitorozása",
"SearchOnAddCollectionHelpText": "Ebben a gyűjteményben található filmek keresése, a hozzáadás adás után",
"UnableToLoadCollections": "Nem sikerült betölteni a gyűjteményeket",
"MonitoredCollectionHelpText": "A gyűjteményből származó filmek monitorozása, hogy automatikusan hozzáadódjanak a könyvtárhoz",
"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",
"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"
"MonitoredCollectionHelpText": "A gyűjteményből származó filmek monitorozása, hogy automatikusan hozzáadódjanak a könyvtárhoz"
}

View File

@@ -1 +0,0 @@
{}

View File

@@ -177,82 +177,5 @@
"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.",
"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"
"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."
}

View File

@@ -246,10 +246,10 @@
"ImportMechanismHealthCheckMessage": "Włącz obsługę ukończonego pobierania",
"IndexersSettingsSummary": "Indeksatory i ograniczenia dotyczące wersji",
"InvalidFormat": "Niepoprawny format",
"ChmodFolder": "chmod Folderu",
"ChmodFolder": "chmod Folder",
"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 Grupy",
"ChmodGroup": "chmod Group",
"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": "Klonuj Tag Formatu",
"CloneIndexer": "Sklonuj Indekser",
"CloneFormatTag": "Clone Format Tag",
"CloneIndexer": "Clone Indexer",
"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 silnikiem programu, aby przywrócić funkcjonalność musi zostać zrestartowany.",
"ConnectionLostMessage": "Radarr utracił połączenie z zapleczem i będzie musiał zostać ponownie załadowany, aby przywrócić funkcjonalność.",
"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 '{0}' i cała jego zawartość zostaną usunięte.",
"DeleteTheMovieFolder": "Folder filmów „{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": "Poprawione i przepakowane",
"DownloadPropersAndRepacks": "Propers and Repacks",
"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": "Pobierz ID",
"GrabRelease": "Pobierz Wydanie",
"GrabID": "Grab ID",
"GrabRelease": "Grab Release",
"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": "Łącze do iCal",
"iCalLink": "iCal Link",
"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": "Z małej litery",
"LowerCase": "Małe 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": "Zanegowane",
"Negated": "Negated",
"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": "Długość",
"Runtime": "Runtime",
"Save": "Zapisać",
"SaveChanges": "Zapisz zmiany",
"SaveSettings": "Zapisz ustawienia",
"Scheduled": "Planowy",
"Score": "Wynik",
"Script": "Scenariusz",
"ScriptPath": "Ścieżka do Skryptu",
"ScriptPath": "Script Path",
"SearchAll": "Wyszukaj wszystko",
"SearchCutoffUnmet": "Niespełnione Parametry Wyszukiwania",
"SearchCutoffUnmet": "Search Cutoff Unmet",
"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": "Zaznacz, jeśli Radarr nie będzie wstanie wykryć ilości wolnego miejsca w główny folderze filmów",
"SkipFreeSpaceCheckWhenImportingHelpText": "Użyj, gdy Radarr nie może wykryć wolnego miejsca w folderze głównym filmu",
"Small": "Mały",
"Socks4": "Skarpetki 4",
"Socks5": "Socks5 (Wsparcie TOR)",
@@ -932,10 +932,10 @@
"Time": "Czas",
"Title": "Tytuł",
"Titles": "Tytuły",
"TMDBId": "Identyfikator TMDb",
"TMDBId": "TMDb Id",
"TmdbIdHelpText": "Identyfikator TMDb filmu do wykluczenia",
"Today": "Dzisiaj",
"TorrentDelay": "Opóźnienie Torrenta",
"TorrentDelay": "Torrent Delay",
"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, interfejsu użytkownika używanego przez Radarr",
"UILanguageHelpText": "Język, którego Radarr będzie używać w interfejsie użytkownika",
"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": "Z dużej litery",
"UpperCase": "Duże 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": "Ostrzeż",
"Warn": "Ostrzec",
"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 dodać folderu głównego",
"UnableToAddRootFolder": "Nie można załadować folderów głównych",
"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ć listy blokowania",
"UnableToLoadBlocklist": "Nie można załadować czarnej listy",
"Blocklisted": "Czarna lista",
"AreYouSureYouWantToRemoveSelectedItemFromQueue": "Czy na pewno chcesz usunąć {0} element {1} z kolejki?",
"BlocklistReleases": "Wydanie czarnej listy",
"Filters": "Filtry",
"Filters": "Filtr",
"LocalPath": "Ścieżka lokalna",
"List": "Lista",
"Rating": "Ocena",
"RemotePath": "Ścieżka zdalna",
"List": "Listy",
"Rating": "Oceny",
"RemotePath": "Zdalna ścieżka",
"SelectLanguages": "Wybierz język",
"AllCollectionsHiddenDueToFilter": "Wszystkie kolekcje są ukryte ze względu na zastosowany filtr.",
"Collections": "Kolekcje",
"Collections": "Kolekcja",
"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ć uprawnienia dostępu do folderu.",
"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.",
"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": "Z Letterboxd",
"Letterboxd": "Letterboxd",
"MonitorCollection": "Monitoruj kolekcję",
"MovieAndCollection": "Film i kolekcja",
"MovieCollectionMissingRoot": "Brak katalogu głównego dla kolekcji filmów: {0}",
@@ -1134,12 +1134,5 @@
"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",
"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"
"Database": "Baza danych"
}

View File

@@ -1116,12 +1116,5 @@
"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.",
"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"
"NoCollections": "Nenhum filme encontrado. Para começar, adiciona um novo filme ou importa alguns já existentes."
}

View File

@@ -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": "Nenhum indexador disponível com a Pesquisa Interativa ativada, o Radarr não fornecerá nenhum resultado de pesquisa interativa",
"IndexerSearchCheckNoInteractiveMessage": "Sem indexadores disponíveis com a Pesquisa Interativa habilitada, o Radarr não fornecerá resultados interativos de pesquisa",
"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 o Radarr quando for impossível detectar o espaço livre da sua pasta raiz do filme",
"SkipFreeSpaceCheckWhenImportingHelpText": "Usar quando o Radarr não conseguir detectar espaço livre da 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 atualizador integrado do Radarr ou um script",
"UpdateMechanismHelpText": "Usar o atualizador embutido 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": "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.",
"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.",
"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",
@@ -1134,13 +1134,5 @@
"MonitoredCollectionHelpText": "Monitorar para que os filmes desta coleção sejam adicionados automaticamente à biblioteca",
"RefreshCollections": "Atualizar Coleções",
"SearchOnAddCollectionHelpText": "Pesquisar filmes nesta coleção quando adicionados à biblioteca",
"ScrollMovies": "Rolar Filmes",
"OnMovieAdded": "Ao Filme Adicionado",
"OnMovieAddedHelpText": "Ao Filme Adicionado",
"ShowPosters": "Mostrar pôsteres",
"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",
"RottenTomatoesRating": "Avaliação Tomato"
"ScrollMovies": "Rolar Filmes"
}

View File

@@ -110,6 +110,5 @@
"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?",
"AllCollectionsHiddenDueToFilter": "Všetky filmy sú skryté kvôli použitému filtru."
"DeleteRestrictionHelpText": "Naozaj chcete zmazať tento profil oneskorenia?"
}

View File

@@ -1134,6 +1134,5 @@
"MovieOnly": "仅电影",
"NoCollections": "没有发现集合,点击添加新的电影或者导入已经存在的电影",
"InstanceNameHelpText": "选项卡及日志应用名称",
"ScrollMovies": "滚动电影",
"CollectionOptions": "Collection Options"
"ScrollMovies": "滚动电影"
}

View File

@@ -1,7 +1,4 @@
{
"About": "關於",
"Add": "添加",
"AcceptConfirmationModal": "接受確認模式",
"Actions": "‎行動‎",
"Activity": "‎活動‎"
"Add": "添加"
}

View File

@@ -91,7 +91,7 @@ namespace NzbDrone.Core.MediaFiles
if (_diskProvider.FolderEmpty(rootFolder))
{
_logger.Warn("Movie's root folder ({0}) is empty. Rescan will not update movies as a failsafe.", rootFolder);
_logger.Warn("Movie's root folder ({0}) is empty.", rootFolder);
_eventAggregator.PublishEvent(new MovieScanSkippedEvent(movie, MovieScanSkippedReason.RootFolderIsEmpty));
return;
}

View File

@@ -5,7 +5,7 @@ using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.Events
{
public class MovieFileImportedEvent : IEvent
public class MovieImportedEvent : 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 MovieFileImportedEvent(LocalMovie movieInfo, MovieFile importedMovie, List<MovieFile> oldFiles, bool newDownload, DownloadClientItem downloadClientItem)
public MovieImportedEvent(LocalMovie movieInfo, MovieFile importedMovie, List<MovieFile> oldFiles, bool newDownload, DownloadClientItem downloadClientItem)
{
MovieInfo = movieInfo;
ImportedMovie = importedMovie;

View File

@@ -61,8 +61,8 @@ namespace NzbDrone.Core.MediaFiles
if (_diskProvider.GetDirectories(rootFolder).Empty())
{
_logger.Warn("Movie's root folder ({0}) is empty. Rescan will not update movies as a failsafe.", rootFolder);
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Movie's root folder ({0}) is empty. Rescan will not update movies as a failsafe.", rootFolder);
_logger.Warn("Movie's root folder ({0}) is empty.", rootFolder);
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Movie's root folder ({0}) is empty.", rootFolder);
}
if (_diskProvider.FolderExists(movie.Path) && _diskProvider.FileExists(fullPath))

View File

@@ -153,10 +153,10 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
return "WMA";
}
Logger.ForDebugEvent()
Logger.Debug()
.Message("Unknown audio format: '{0}' in '{1}'.", mediaInfo.RawStreamData, sceneName)
.WriteSentryWarn("UnknownAudioFormatFFProbe", mediaInfo.ContainerFormat, mediaInfo.AudioFormat, audioCodecID)
.Log();
.Write();
return mediaInfo.AudioFormat;
}
@@ -262,10 +262,10 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
return "";
}
Logger.ForDebugEvent()
Logger.Debug()
.Message("Unknown video format: '{0}' in '{1}'.", mediaInfo.RawStreamData, sceneName)
.WriteSentryWarn("UnknownVideoFormatFFProbe", mediaInfo.ContainerFormat, videoFormat, videoCodecID)
.Log();
.Write();
return result;
}

View File

@@ -144,7 +144,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
_extraService.ImportMovie(localMovie, movieFile, copyOnly);
}
_eventAggregator.PublishEvent(new MovieFileImportedEvent(localMovie, movieFile, oldFiles, newDownload, downloadClientItem));
_eventAggregator.PublishEvent(new MovieImportedEvent(localMovie, movieFile, oldFiles, newDownload, downloadClientItem));
}
catch (RootFolderNotFoundException e)
{

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Movies.Events;
@@ -14,7 +13,6 @@ 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);
@@ -25,14 +23,12 @@ 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, Logger logger)
public MovieCollectionService(IMovieCollectionRepository repo, IMovieService movieService, IEventAggregator eventAggregator)
{
_repo = repo;
_movieService = movieService;
_eventAggregator = eventAggregator;
_logger = logger;
}
public MovieCollection AddCollection(MovieCollection newCollection)
@@ -77,21 +73,6 @@ 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);

View File

@@ -5,11 +5,11 @@ namespace NzbDrone.Core.Movies.Events
{
public class MoviesImportedEvent : IEvent
{
public List<Movie> Movies { get; private set; }
public List<int> MovieIds { get; private set; }
public MoviesImportedEvent(List<Movie> movies)
public MoviesImportedEvent(List<int> movieIds)
{
Movies = movies;
MovieIds = movieIds;
}
}
}

View File

@@ -23,7 +23,7 @@ namespace NzbDrone.Core.Movies
public void Handle(MoviesImportedEvent message)
{
_commandQueueManager.PushMany(message.Movies.Select(s => new RefreshMovieCommand(new List<int> { s.Id }, true)).ToList());
_commandQueueManager.PushMany(message.MovieIds.Select(s => new RefreshMovieCommand(new List<int> { s }, true)).ToList());
}
}
}

View File

@@ -100,7 +100,7 @@ namespace NzbDrone.Core.Movies
{
_movieRepository.InsertMany(newMovies);
_eventAggregator.PublishEvent(new MoviesImportedEvent(newMovies));
_eventAggregator.PublishEvent(new MoviesImportedEvent(newMovies.Select(s => s.Id).ToList()));
return newMovies;
}

View File

@@ -12,7 +12,7 @@ using NzbDrone.Core.Movies.Events;
namespace NzbDrone.Core.Movies
{
public class RefreshCollectionService : IExecute<RefreshCollectionsCommand>
public class RefreshCollectionService : IExecute<RefreshCollectionsCommand>, IHandle<CollectionEditedEvent>
{
private readonly IProvideMovieInfo _movieInfo;
private readonly IMovieCollectionService _collectionService;
@@ -99,25 +99,21 @@ namespace NzbDrone.Core.Movies
{
var existingMovies = _movieService.AllMovieTmdbIds();
var collectionMovies = _movieMetadataService.GetMoviesByCollectionTmdbId(collection.TmdbId);
var moviesToAdd = collectionMovies.Where(m => !existingMovies.Contains(m.TmdbId));
if (moviesToAdd.Any())
_addMovieService.AddMovies(collectionMovies.Where(m => !existingMovies.Contains(m.TmdbId)).Select(m => new Movie
{
_addMovieService.AddMovies(moviesToAdd.Select(m => new Movie
TmdbId = m.TmdbId,
Title = m.Title,
ProfileId = collection.QualityProfileId,
RootFolderPath = collection.RootFolderPath,
MinimumAvailability = collection.MinimumAvailability,
AddOptions = new AddMovieOptions
{
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());
}
SearchForMovie = collection.SearchOnAdd,
AddMethod = AddMovieMethod.Collection
},
Monitored = true
}).ToList());
}
}
@@ -155,5 +151,10 @@ namespace NzbDrone.Core.Movies
}
}
}
public void Handle(CollectionEditedEvent message)
{
SyncCollectionMovies(message.Collection);
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using NzbDrone.Core.Update;
namespace NzbDrone.Core.Notifications
{
@@ -7,6 +8,7 @@ namespace NzbDrone.Core.Notifications
public string Message { get; set; }
public Version PreviousVersion { get; set; }
public Version NewVersion { get; set; }
public UpdateChanges Changes { get; set; }
public override string ToString()
{

View File

@@ -212,6 +212,16 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Radarr_Update_NewVersion", updateMessage.NewVersion.ToString());
environmentVariables.Add("Radarr_Update_PreviousVersion", updateMessage.PreviousVersion.ToString());
if (updateMessage.Changes.New.Any())
{
environmentVariables.Add("Radarr_Update_NewChanges", string.Join("|", updateMessage.Changes.New.Select(e => e)));
}
if (updateMessage.Changes.Fixed.Any())
{
environmentVariables.Add("Radarr_Update_FixedChanges", string.Join("|", updateMessage.Changes.Fixed.Select(e => e)));
}
ExecuteScript(environmentVariables);
}

View File

@@ -317,6 +317,16 @@ namespace NzbDrone.Core.Notifications.Discord
{
Name = "New Version",
Value = updateMessage.NewVersion.ToString()
},
new DiscordField()
{
Name = "New",
Value = string.Format("```{0}```", string.Join(Environment.NewLine, updateMessage.Changes.New))
},
new DiscordField()
{
Name = "Fixed",
Value = string.Format("```{0}```", string.Join(Environment.NewLine, updateMessage.Changes.Fixed))
}
},
}

View File

@@ -6,7 +6,6 @@ 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
@@ -78,15 +77,6 @@ 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);
@@ -186,6 +176,16 @@ namespace NzbDrone.Core.Notifications.Notifiarr
variables.Add("Radarr_Update_NewVersion", updateMessage.NewVersion.ToString());
variables.Add("Radarr_Update_PreviousVersion", updateMessage.PreviousVersion.ToString());
if (updateMessage.Changes.New.Any())
{
variables.Add("Radarr_Update_NewChanges", string.Join("|", updateMessage.Changes.New.Select(e => e)));
}
if (updateMessage.Changes.Fixed.Any())
{
variables.Add("Radarr_Update_FixedChanges", string.Join("|", updateMessage.Changes.Fixed.Select(e => e)));
}
_proxy.SendNotification(variables, Settings);
}

View File

@@ -78,7 +78,8 @@ 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 + "/api/v1/notification/radarr/" + settings.APIKey).Post();
var requestBuilder = new HttpRequestBuilder(url + "/notifier.php").Post();
requestBuilder.AddFormParameter("api", settings.APIKey).Build();
requestBuilder.AddFormParameter("instanceName", settings.InstanceName).Build();
foreach (string key in message.Keys)

View File

@@ -85,7 +85,6 @@ namespace NzbDrone.Core.Notifications
definition.SupportsOnDownload = provider.SupportsOnDownload;
definition.SupportsOnUpgrade = provider.SupportsOnUpgrade;
definition.SupportsOnRename = provider.SupportsOnRename;
definition.SupportsOnMovieAdded = provider.SupportsOnMovieAdded;
definition.SupportsOnMovieDelete = provider.SupportsOnMovieDelete;
definition.SupportsOnMovieFileDelete = provider.SupportsOnMovieFileDelete;
definition.SupportsOnMovieFileDeleteForUpgrade = provider.SupportsOnMovieFileDeleteForUpgrade;

View File

@@ -17,10 +17,9 @@ namespace NzbDrone.Core.Notifications
public class NotificationService
: IHandle<MovieRenamedEvent>,
IHandle<MovieGrabbedEvent>,
IHandle<MovieFileImportedEvent>,
IHandle<MovieImportedEvent>,
IHandle<MoviesDeletedEvent>,
IHandle<MovieAddedEvent>,
IHandle<MoviesImportedEvent>,
IHandle<MovieFileDeletedEvent>,
IHandle<HealthCheckFailedEvent>,
IHandle<UpdateInstalledEvent>,
@@ -120,7 +119,7 @@ namespace NzbDrone.Core.Notifications
}
}
public void Handle(MovieFileImportedEvent message)
public void Handle(MovieImportedEvent message)
{
if (!message.NewDownload)
{
@@ -175,27 +174,6 @@ 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())
@@ -220,6 +198,7 @@ namespace NzbDrone.Core.Notifications
updateMessage.Message = $"Radarr updated from {message.PreviousVerison.ToString()} to {message.NewVersion.ToString()}";
updateMessage.PreviousVersion = message.PreviousVerison;
updateMessage.NewVersion = message.NewVersion;
updateMessage.Changes = message.Changes;
foreach (var notification in _notificationFactory.OnApplicationUpdateEnabled())
{

View File

@@ -57,15 +57,15 @@ namespace NzbDrone.Core.Notifications.Plex.Server
var partialUpdates = _partialUpdateCache.Get(settings.Host, () => PartialUpdatesAllowed(settings, version), TimeSpan.FromHours(2));
var pathScanCache = _pathScanCache.Get(settings.Host, () => PathUpdatesAllowed(settings, version), TimeSpan.FromHours(2));
var pathsUpdated = true;
var pathUpdated = true;
if (pathScanCache)
{
foreach (var movie in multipleMovies)
{
pathsUpdated &= UpdatePath(movie, sections, settings);
pathUpdated &= UpdatePath(movie, sections, settings);
if (!pathsUpdated)
if (!pathUpdated)
{
break;
}
@@ -73,7 +73,7 @@ namespace NzbDrone.Core.Notifications.Plex.Server
}
// If we couldn't path update then try partial and full update
if (!pathScanCache || (pathScanCache && !pathsUpdated))
if (!pathUpdated)
{
if (partialUpdates)
{
@@ -244,10 +244,6 @@ namespace NzbDrone.Core.Notifications.Plex.Server
pathUpdated = true;
}
else
{
_logger.Debug("Unable to update path, mapped movie path {0} is not a child of any plex libraries: {1}", mappedPath, sections.SelectMany(s => s.Locations.Select(l => l.Path)).Join(", "));
}
return pathUpdated;
}

View File

@@ -136,7 +136,9 @@ namespace NzbDrone.Core.Notifications.Webhook
EventType = WebhookEventType.ApplicationUpdate,
Message = updateMessage.Message,
PreviousVersion = updateMessage.PreviousVersion.ToString(),
NewVersion = updateMessage.NewVersion.ToString()
NewVersion = updateMessage.NewVersion.ToString(),
NewChanges = updateMessage.Changes.New,
FixedChanges = updateMessage.Changes.Fixed
};
_proxy.SendWebhook(payload, Settings);

View File

@@ -1,3 +1,5 @@
using System.Collections.Generic;
namespace NzbDrone.Core.Notifications.Webhook
{
public class WebhookApplicationUpdatePayload : WebhookPayload
@@ -5,5 +7,7 @@ namespace NzbDrone.Core.Notifications.Webhook
public string Message { get; set; }
public string PreviousVersion { get; set; }
public string NewVersion { get; set; }
public List<string> NewChanges { get; set; }
public List<string> FixedChanges { get; set; }
}
}

View File

@@ -41,8 +41,7 @@ namespace NzbDrone.Core.Parser
new IsoLanguage("ar", "", "ara", "Arabic", Language.Arabic),
new IsoLanguage("uk", "", "ukr", "Ukrainian", Language.Ukrainian),
new IsoLanguage("fa", "", "fas", "Persian", Language.Persian),
new IsoLanguage("be", "", "ben", "Bengali", Language.Bengali),
new IsoLanguage("lt", "", "lit", "Lithuanian", Language.Lithuanian)
new IsoLanguage("be", "", "ben", "Bengali", Language.Bengali)
};
public static IsoLanguage Find(string isoCode)

View File

@@ -20,18 +20,6 @@ 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),
@@ -53,7 +41,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[]
@@ -109,7 +97,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*?(?![a-b0-9])",
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*?",
string.Empty,
RegexOptions.IgnoreCase | RegexOptions.Compiled);

View File

@@ -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)|

View File

@@ -19,7 +19,7 @@
<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="5.0.1" />
<PackageReference Include="NLog" Version="4.7.14" />
<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" />

View File

@@ -7,11 +7,13 @@ namespace NzbDrone.Core.Update.History.Events
{
public Version PreviousVerison { get; set; }
public Version NewVersion { get; set; }
public UpdateChanges Changes { get; set; }
public UpdateInstalledEvent(Version previousVersion, Version newVersion)
public UpdateInstalledEvent(Version previousVersion, Version newVersion, UpdateChanges changes)
{
PreviousVerison = previousVersion;
NewVersion = newVersion;
Changes = changes;
}
}
}

View File

@@ -1,7 +1,9 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Update.History.Events;
@@ -17,13 +19,21 @@ namespace NzbDrone.Core.Update.History
public class UpdateHistoryService : IUpdateHistoryService, IHandle<ApplicationStartedEvent>, IHandleAsync<ApplicationStartedEvent>
{
private readonly IUpdateHistoryRepository _repository;
private readonly IConfigFileProvider _configFileProvider;
private readonly IUpdatePackageProvider _updatePackageProvider;
private readonly IEventAggregator _eventAggregator;
private readonly Logger _logger;
private Version _prevVersion;
public UpdateHistoryService(IUpdateHistoryRepository repository, IEventAggregator eventAggregator, Logger logger)
public UpdateHistoryService(IUpdateHistoryRepository repository,
IConfigFileProvider configFileProvider,
IUpdatePackageProvider updatePackageProvider,
IEventAggregator eventAggregator,
Logger logger)
{
_repository = repository;
_configFileProvider = configFileProvider;
_updatePackageProvider = updatePackageProvider;
_eventAggregator = eventAggregator;
_logger = logger;
}
@@ -93,7 +103,20 @@ namespace NzbDrone.Core.Update.History
{
if (_prevVersion != null)
{
_eventAggregator.PublishEvent(new UpdateInstalledEvent(_prevVersion, BuildInfo.Version));
var branch = _configFileProvider.Branch;
var version = BuildInfo.Version;
var packageChanges = _updatePackageProvider.GetRecentUpdates(branch, version, _prevVersion)
.Select(u => u.Changes);
var changes = new UpdateChanges();
foreach (var change in packageChanges)
{
changes.New = change.New.Union(change.New).ToList();
changes.Fixed = change.Fixed.Union(change.Fixed).ToList();
}
_eventAggregator.PublishEvent(new UpdateInstalledEvent(_prevVersion, BuildInfo.Version, changes));
}
}
}

View File

@@ -9,7 +9,7 @@ namespace NzbDrone.Core.Validation.Paths
private readonly IAppFolderInfo _appFolderInfo;
public StartupFolderValidator(IAppFolderInfo appFolderInfo)
: base("Path cannot be {relationship} the start up folder")
: base("Path cannot be an ancestor of the start up folder")
{
_appFolderInfo = appFolderInfo;
}
@@ -21,24 +21,7 @@ namespace NzbDrone.Core.Validation.Paths
return true;
}
var startupFolder = _appFolderInfo.StartUpFolder;
var folder = context.PropertyValue.ToString();
if (startupFolder.PathEquals(folder))
{
context.MessageFormatter.AppendArgument("relationship", "set to");
return false;
}
if (startupFolder.IsParentPath(folder))
{
context.MessageFormatter.AppendArgument("relationship", "child of");
return false;
}
return true;
return !_appFolderInfo.StartUpFolder.IsParentPath(context.PropertyValue.ToString());
}
}
}

View File

@@ -53,7 +53,6 @@ namespace Radarr.Host
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
var appMode = GetApplicationMode(startupContext);
var config = GetConfiguration(startupContext);
switch (appMode)
{
@@ -82,22 +81,12 @@ namespace Radarr.Host
// Utility mode
default:
{
new HostBuilder()
.UseServiceProviderFactory(new DryIocServiceProviderFactory(new Container(rules => rules.WithNzbDroneRules())))
.ConfigureContainer<IContainer>(c =>
{
c.AutoAddServices(Bootstrap.ASSEMBLIES)
.AddNzbDroneLogger()
.AddDatabase()
.AddStartupContext(startupContext)
.Resolve<UtilityModeRouter>()
.Route(appMode);
})
.ConfigureServices(services =>
{
services.Configure<PostgresOptions>(config.GetSection("Radarr:Postgres"));
}).Build();
new Container(rules => rules.WithNzbDroneRules())
.AutoAddServices(ASSEMBLIES)
.AddNzbDroneLogger()
.AddStartupContext(startupContext)
.Resolve<UtilityModeRouter>()
.Route(appMode);
break;
}
}

View File

@@ -3,11 +3,17 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using DryIoc;
using System.Runtime.CompilerServices;
using Moq;
using Moq.Language.Flow;
using NzbDrone.Common.Composition;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Test.Common.AutoMoq.Unity;
using Unity;
[assembly: InternalsVisibleTo("AutoMoq.Tests")]
namespace NzbDrone.Test.Common.AutoMoq
{
@@ -15,18 +21,32 @@ namespace NzbDrone.Test.Common.AutoMoq
public class AutoMoqer
{
public readonly MockBehavior DefaultBehavior = MockBehavior.Default;
private IContainer _container;
public Type ResolveType;
private IUnityContainer _container;
private IDictionary<Type, object> _registeredMocks;
public AutoMoqer()
{
SetupAutoMoqer(CreateTestContainer(new Container()));
SetupAutoMoqer(new UnityContainer());
}
public AutoMoqer(MockBehavior defaultBehavior)
{
DefaultBehavior = defaultBehavior;
SetupAutoMoqer(new UnityContainer());
}
public AutoMoqer(IUnityContainer container)
{
SetupAutoMoqer(container);
}
public virtual T Resolve<T>()
{
ResolveType = typeof(T);
var result = _container.Resolve<T>();
SetConstant(result);
ResolveType = null;
return result;
}
@@ -39,6 +59,7 @@ namespace NzbDrone.Test.Common.AutoMoq
public virtual Mock<T> GetMock<T>(MockBehavior behavior)
where T : class
{
ResolveType = null;
var type = GetTheMockType<T>();
if (GetMockHasNotBeenCalledForThisType(type))
{
@@ -57,83 +78,90 @@ namespace NzbDrone.Test.Common.AutoMoq
public virtual void SetMock(Type type, Mock mock)
{
if (GetMockHasNotBeenCalledForThisType(type))
if (_registeredMocks.ContainsKey(type) == false)
{
_registeredMocks.Add(type, mock);
}
if (mock != null)
{
_container.RegisterInstance(type, mock.Object, ifAlreadyRegistered: IfAlreadyRegistered.Replace);
_container.RegisterInstance(type, mock.Object);
}
}
public virtual void SetConstant<T>(T instance)
{
_container.RegisterInstance(instance, ifAlreadyRegistered: IfAlreadyRegistered.Replace);
_container.RegisterInstance(instance);
SetMock(instance.GetType(), null);
}
private IContainer CreateTestContainer(IContainer container)
public ISetup<T> Setup<T>(Expression<Action<T>> expression)
where T : class
{
var c = container.CreateChild(IfAlreadyRegistered.Replace,
container.Rules
.WithDynamicRegistration((serviceType, serviceKey) =>
{
// ignore services with non-default key
if (serviceKey != null)
{
return null;
}
if (serviceType == typeof(object))
{
return null;
}
if (serviceType.IsGenericType && serviceType.IsOpenGeneric())
{
return null;
}
// get the Mock object for the abstract class or interface
if (serviceType.IsInterface || serviceType.IsAbstract)
{
var mockType = typeof(Mock<>).MakeGenericType(serviceType);
var mockFactory = new DelegateFactory(r =>
{
var mock = (Mock)r.Resolve(mockType);
SetMock(serviceType, mock);
return mock.Object;
}, Reuse.Singleton);
return new[] { new DynamicRegistration(mockFactory, IfAlreadyRegistered.Keep) };
}
// concrete types
var concreteTypeFactory = serviceType.ToFactory(Reuse.Singleton, FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublic);
return new[] { new DynamicRegistration(concreteTypeFactory) };
},
DynamicRegistrationFlags.Service | DynamicRegistrationFlags.AsFallback));
c.Register(typeof(Mock<>), Reuse.Singleton, FactoryMethod.DefaultConstructor());
return c;
return GetMock<T>().Setup(expression);
}
private void SetupAutoMoqer(IContainer container)
public ISetup<T, TResult> Setup<T, TResult>(Expression<Func<T, TResult>> expression)
where T : class
{
return GetMock<T>().Setup(expression);
}
public void Verify<T>(Expression<Action<T>> expression)
where T : class
{
GetMock<T>().Verify(expression);
}
public void Verify<T>(Expression<Action<T>> expression, string failMessage)
where T : class
{
GetMock<T>().Verify(expression, failMessage);
}
public void Verify<T>(Expression<Action<T>> expression, Times times)
where T : class
{
GetMock<T>().Verify(expression, times);
}
public void Verify<T>(Expression<Action<T>> expression, Times times, string failMessage)
where T : class
{
GetMock<T>().Verify(expression, times, failMessage);
}
public void VerifyAllMocks()
{
foreach (var registeredMock in _registeredMocks)
{
var mock = registeredMock.Value as Mock;
if (mock != null)
{
mock.VerifyAll();
}
}
}
private void SetupAutoMoqer(IUnityContainer container)
{
_container = container;
container.RegisterInstance(this);
_registeredMocks = new Dictionary<Type, object>();
LoadPlatformLibrary();
RegisterPlatformLibrary(container);
AddTheAutoMockingContainerExtensionToTheContainer(container);
AssemblyLoader.RegisterNativeResolver(new[] { "System.Data.SQLite", "Radarr.Core" });
}
private static void AddTheAutoMockingContainerExtensionToTheContainer(IUnityContainer container)
{
container.AddNewExtension<AutoMockingContainerExtension>();
return;
}
private Mock<T> TheRegisteredMockForThisType<T>(Type type)
where T : class
{
@@ -150,7 +178,7 @@ namespace NzbDrone.Test.Common.AutoMoq
private bool GetMockHasNotBeenCalledForThisType(Type type)
{
return !_registeredMocks.ContainsKey(type);
return _registeredMocks.ContainsKey(type) == false;
}
private static Type GetTheMockType<T>()
@@ -159,7 +187,7 @@ namespace NzbDrone.Test.Common.AutoMoq
return typeof(T);
}
private void LoadPlatformLibrary()
private void RegisterPlatformLibrary(IUnityContainer container)
{
var assemblyName = "Radarr.Windows";

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Moq;
using Unity;
using Unity.Builder;
using Unity.Strategies;
namespace NzbDrone.Test.Common.AutoMoq.Unity
{
public class AutoMockingBuilderStrategy : BuilderStrategy
{
private readonly IUnityContainer _container;
private readonly MockRepository _mockFactory;
private readonly IEnumerable<Type> _registeredTypes;
public AutoMockingBuilderStrategy(IEnumerable<Type> registeredTypes, IUnityContainer container)
{
var autoMoqer = container.Resolve<AutoMoqer>();
_mockFactory = new MockRepository(autoMoqer.DefaultBehavior);
_registeredTypes = registeredTypes;
_container = container;
}
public override void PreBuildUp(ref BuilderContext context)
{
var autoMoqer = _container.Resolve<AutoMoqer>();
var type = GetTheTypeFromTheBuilderContext(context);
if (AMockObjectShouldBeCreatedForThisType(type))
{
var mock = CreateAMockObject(type);
context.Existing = mock.Object;
autoMoqer.SetMock(type, mock);
}
}
private bool AMockObjectShouldBeCreatedForThisType(Type type)
{
var mocker = _container.Resolve<AutoMoqer>();
return TypeIsNotRegistered(type) && (mocker.ResolveType == null || mocker.ResolveType != type);
}
private static Type GetTheTypeFromTheBuilderContext(BuilderContext context)
{
// return (context.OriginalBuildKey).Type;
return context.Type;
}
private bool TypeIsNotRegistered(Type type)
{
return _registeredTypes.Any(x => x.Equals(type)) == false;
}
private Mock CreateAMockObject(Type type)
{
var createMethod = GenerateAnInterfaceMockCreationMethod(type);
return InvokeTheMockCreationMethod(createMethod);
}
private Mock InvokeTheMockCreationMethod(MethodInfo createMethod)
{
return (Mock)createMethod.Invoke(_mockFactory, new object[] { new List<object>().ToArray() });
}
private MethodInfo GenerateAnInterfaceMockCreationMethod(Type type)
{
var createMethodWithNoParameters = _mockFactory.GetType().GetMethod("Create", EmptyArgumentList());
return createMethodWithNoParameters.MakeGenericMethod(new[] { type });
}
private static Type[] EmptyArgumentList()
{
return new[] { typeof(object[]) };
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using Unity.Builder;
using Unity.Extension;
namespace NzbDrone.Test.Common.AutoMoq.Unity
{
public class AutoMockingContainerExtension : UnityContainerExtension
{
private readonly IList<Type> _registeredTypes = new List<Type>();
protected override void Initialize()
{
SetEventsOnContainerToTrackAllRegisteredTypes();
SetBuildingStrategyForBuildingUnregisteredTypes();
}
private void SetEventsOnContainerToTrackAllRegisteredTypes()
{
Context.Registering += (sender, e) => RegisterType(e.TypeFrom);
Context.RegisteringInstance += (sender, e) => RegisterType(e.RegisteredType);
}
private void RegisterType(Type typeToRegister)
{
_registeredTypes.Add(typeToRegister);
}
private void SetBuildingStrategyForBuildingUnregisteredTypes()
{
var strategy = new AutoMockingBuilderStrategy(_registeredTypes, Container);
Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
}
}
}

View File

@@ -6,9 +6,10 @@
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="FluentValidation" Version="8.6.2" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="NLog" Version="4.7.14" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="RestSharp" Version="106.15.0" />
<PackageReference Include="Unity" Version="5.11.10" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NzbDrone.Common\Radarr.Common.csproj" />

View File

@@ -6,7 +6,7 @@
<ItemGroup>
<PackageReference Include="DryIoc.dll" Version="4.8.8" />
<PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="5.1.0" />
<PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="NLog" Version="4.7.14" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NzbDrone.Common\Radarr.Common.csproj" />

View File

@@ -4,7 +4,7 @@
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="NLog" Version="4.7.14" />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,13 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Collections;
using NzbDrone.Core.Movies.Commands;
using NzbDrone.Core.Movies.Events;
using NzbDrone.Core.Organizer;
using NzbDrone.SignalR;
@@ -28,15 +25,13 @@ namespace Radarr.Api.V3.Collections
private readonly IMovieMetadataService _movieMetadataService;
private readonly IBuildFileNames _fileNameBuilder;
private readonly INamingConfigService _namingService;
private readonly IManageCommandQueue _commandQueueManager;
public CollectionController(IBroadcastSignalRMessage signalRBroadcaster,
IMovieCollectionService collectionService,
IMovieService movieService,
IMovieMetadataService movieMetadataService,
IBuildFileNames fileNameBuilder,
INamingConfigService namingService,
IManageCommandQueue commandQueueManager)
INamingConfigService namingService)
: base(signalRBroadcaster)
{
_collectionService = collectionService;
@@ -44,7 +39,6 @@ namespace Radarr.Api.V3.Collections
_movieMetadataService = movieMetadataService;
_fileNameBuilder = fileNameBuilder;
_namingService = namingService;
_commandQueueManager = commandQueueManager;
}
protected override CollectionResource GetResourceById(int id)
@@ -73,47 +67,34 @@ namespace Radarr.Api.V3.Collections
}
[HttpPut]
public ActionResult UpdateCollections(CollectionUpdateResource resource)
public ActionResult UpdateCollections(CollectionUpdateResource collectionResources)
{
var collectionsToUpdate = _collectionService.GetCollections(resource.CollectionIds);
var collectionsToUpdate = _collectionService.GetCollections(collectionResources.Collections.Select(c => c.Id));
var update = new List<CollectionResource>();
foreach (var collection in collectionsToUpdate)
foreach (var c in collectionResources.Collections)
{
if (resource.Monitored.HasValue)
var collection = collectionsToUpdate.Single(n => n.Id == c.Id);
if (c.Monitored.HasValue)
{
collection.Monitored = resource.Monitored.Value;
collection.Monitored = c.Monitored.Value;
}
if (resource.QualityProfileId.HasValue)
{
collection.QualityProfileId = resource.QualityProfileId.Value;
}
if (resource.MinimumAvailability.HasValue)
{
collection.MinimumAvailability = resource.MinimumAvailability.Value;
}
if (resource.RootFolderPath.IsNotNullOrWhiteSpace())
{
collection.RootFolderPath = resource.RootFolderPath;
}
if (resource.MonitorMovies.HasValue)
if (collectionResources.MonitorMovies.HasValue)
{
var movies = _movieService.GetMoviesByCollectionTmdbId(collection.TmdbId);
movies.ForEach(c => c.Monitored = resource.MonitorMovies.Value);
movies.ForEach(c => c.Monitored = collectionResources.MonitorMovies.Value);
_movieService.UpdateMovie(movies, true);
}
var updatedCollection = _collectionService.UpdateCollection(collection);
update.Add(updatedCollection.ToResource());
}
var updated = _collectionService.UpdateCollections(collectionsToUpdate.ToList()).ToResource();
_commandQueueManager.Push(new RefreshCollectionsCommand());
return Accepted(updated);
return Accepted(update);
}
private IEnumerable<CollectionResource> MapToResource(List<MovieCollection> collections, List<MovieMetadata> collectionMovies)
@@ -144,7 +125,7 @@ namespace Radarr.Api.V3.Collections
foreach (var movie in _movieMetadataService.GetMoviesByCollectionTmdbId(collection.TmdbId))
{
var movieResource = movie.ToResource();
movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { MovieMetadata = movie });
movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { Title = movie.Title, Year = movie.Year, ImdbId = movie.ImdbId, TmdbId = movie.TmdbId });
resource.Movies.Add(movieResource);
}

View File

@@ -1,16 +1,11 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Movies;
namespace Radarr.Api.V3.Collections
{
public class CollectionUpdateResource
{
public List<int> CollectionIds { get; set; }
public bool? Monitored { get; set; }
public List<CollectionUpdateCollectionResource> Collections { get; set; }
public bool? MonitorMovies { get; set; }
public int? QualityProfileId { get; set; }
public string RootFolderPath { get; set; }
public MovieStatusType? MinimumAvailability { get; set; }
}
}

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