mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-15 15:54:47 -04:00
Compare commits
29 Commits
collection
...
v5.1.0.817
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79c03f2fe6 | ||
|
|
9b36404071 | ||
|
|
ecfaea3885 | ||
|
|
bfbeb4c62e | ||
|
|
4b98d27f31 | ||
|
|
604d74270d | ||
|
|
15bb9139d1 | ||
|
|
32722eb704 | ||
|
|
e0c8a8f0d6 | ||
|
|
a3bb0541f0 | ||
|
|
e78bc34514 | ||
|
|
35c4538288 | ||
|
|
3981e816cd | ||
|
|
9354031571 | ||
|
|
a01328dc8c | ||
|
|
8cb6295ddc | ||
|
|
99f7d8bcf5 | ||
|
|
f13d479b88 | ||
|
|
23eb637bc3 | ||
|
|
3a786d0b9d | ||
|
|
6fb127235c | ||
|
|
5517e578b6 | ||
|
|
bced2e7b2e | ||
|
|
f7313369b5 | ||
|
|
b14e93e11f | ||
|
|
f5692d6cf1 | ||
|
|
a2d505c795 | ||
|
|
3d46bd2d8f | ||
|
|
017f272201 |
@@ -9,7 +9,7 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '5.0.3'
|
||||
majorVersion: '5.1.0'
|
||||
minorVersion: $[counter('minorVersion', 2000)]
|
||||
radarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import AppSectionState from 'App/State/AppSectionState';
|
||||
import MovieCollection from 'typings/MovieCollection';
|
||||
|
||||
type MovieCollectionAppState = AppSectionState<MovieCollection>;
|
||||
interface MovieCollectionAppState extends AppSectionState<MovieCollection> {
|
||||
itemMap: Record<number, number>;
|
||||
}
|
||||
|
||||
export default MovieCollectionAppState;
|
||||
|
||||
@@ -42,9 +42,9 @@ function Agenda(props) {
|
||||
<div className={styles.agenda}>
|
||||
{
|
||||
items.map((item, index) => {
|
||||
const momentDate = moment(item.inCinemas);
|
||||
const momentDate = moment(item.sortDate);
|
||||
const showDate = index === 0 ||
|
||||
!moment(items[index - 1].inCinemas).isSame(momentDate, 'day');
|
||||
!moment(items[index - 1].sortDate).isSame(momentDate, 'day');
|
||||
|
||||
return (
|
||||
<AgendaEventConnector
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointSmall) {
|
||||
.event {
|
||||
.overlay {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -111,5 +111,4 @@
|
||||
.releaseIcon {
|
||||
margin-right: 20px;
|
||||
width: 25px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class AgendaEvent extends Component {
|
||||
|
||||
<div className={styles.overlay}>
|
||||
<div className={styles.date}>
|
||||
{(showDate) ? startTime.format(longDateFormat) : null}
|
||||
{showDate ? startTime.format(longDateFormat) : null}
|
||||
</div>
|
||||
|
||||
<div className={styles.releaseIcon}>
|
||||
|
||||
@@ -21,6 +21,7 @@ function createMapStateToProps() {
|
||||
|
||||
return {
|
||||
...collection,
|
||||
movies: [...collection.movies].sort((a, b) => b.year - a.year),
|
||||
genres: Array.from(new Set(allGenres)).slice(0, 3)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ class CollectionMovie extends Component {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
status,
|
||||
overview,
|
||||
year,
|
||||
tmdbId,
|
||||
@@ -123,11 +124,11 @@ class CollectionMovie extends Component {
|
||||
|
||||
<div className={styles.overlay}>
|
||||
<div className={styles.overlayTitle}>
|
||||
{title}
|
||||
{title} {year > 0 ? `(${year})` : ''}
|
||||
</div>
|
||||
|
||||
{
|
||||
id &&
|
||||
id ?
|
||||
<div className={styles.overlayStatus}>
|
||||
<MovieIndexProgressBar
|
||||
monitored={monitored}
|
||||
@@ -138,7 +139,8 @@ class CollectionMovie extends Component {
|
||||
detailedProgressBar={detailedProgressBar}
|
||||
isAvailable={isAvailable}
|
||||
/>
|
||||
</div>
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
</Link>
|
||||
@@ -171,6 +173,7 @@ CollectionMovie.propTypes = {
|
||||
id: PropTypes.number,
|
||||
title: PropTypes.string.isRequired,
|
||||
year: PropTypes.number.isRequired,
|
||||
status: PropTypes.string.isRequired,
|
||||
overview: PropTypes.string.isRequired,
|
||||
monitored: PropTypes.bool,
|
||||
collectionId: PropTypes.number.isRequired,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
margin: 2px 4px;
|
||||
border: 1px solid var(--borderColor);
|
||||
border-radius: 4px;
|
||||
background-color: #eee;
|
||||
background-color: var(--inputBackgroundColor);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
padding: 0 4px;
|
||||
border-left: 4px;
|
||||
border-left-style: solid;
|
||||
background-color: var(--white);
|
||||
background-color: var(--themeLightColor);
|
||||
color: var(--defaultColor);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ class CollectionMovieLabel extends Component {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
year,
|
||||
status,
|
||||
monitored,
|
||||
isAvailable,
|
||||
@@ -35,9 +36,7 @@ class CollectionMovieLabel extends Component {
|
||||
}
|
||||
|
||||
<span>
|
||||
{
|
||||
title
|
||||
}
|
||||
{title} {year > 0 ? `(${year})` : ''}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -62,6 +61,7 @@ class CollectionMovieLabel extends Component {
|
||||
CollectionMovieLabel.propTypes = {
|
||||
id: PropTypes.number,
|
||||
title: PropTypes.string.isRequired,
|
||||
year: PropTypes.number.isRequired,
|
||||
status: PropTypes.string,
|
||||
isAvailable: PropTypes.bool,
|
||||
monitored: PropTypes.bool,
|
||||
|
||||
@@ -28,7 +28,6 @@ function calculatePosterWidth(posterSize, isSmallScreen) {
|
||||
}
|
||||
|
||||
function calculateRowHeight(posterHeight, sortKey, isSmallScreen, overviewOptions) {
|
||||
|
||||
const heights = [
|
||||
overviewOptions.showPosters ? posterHeight : 75,
|
||||
isSmallScreen ? columnPaddingSmallScreen : columnPadding
|
||||
@@ -122,8 +121,8 @@ class CollectionOverviews extends Component {
|
||||
overviewOptions
|
||||
} = this.props;
|
||||
|
||||
const posterWidth = calculatePosterWidth(overviewOptions.size, isSmallScreen);
|
||||
const posterHeight = calculatePosterHeight(posterWidth);
|
||||
const posterWidth = overviewOptions.showPosters ? calculatePosterWidth(overviewOptions.size, isSmallScreen) : 0;
|
||||
const posterHeight = overviewOptions.showPosters ? calculatePosterHeight(posterWidth) : 0;
|
||||
const rowHeight = calculateRowHeight(posterHeight, sortKey, isSmallScreen, overviewOptions);
|
||||
|
||||
this.setState({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.tag {
|
||||
height: 21px;
|
||||
display: flex;
|
||||
|
||||
&.isLastTag {
|
||||
.or {
|
||||
@@ -18,4 +18,5 @@
|
||||
.or {
|
||||
margin: 0 3px;
|
||||
color: var(--themeDarkColor);
|
||||
line-height: 31px;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import styles from './FilterBuilderRowValueTag.css';
|
||||
|
||||
function FilterBuilderRowValueTag(props) {
|
||||
return (
|
||||
<span
|
||||
<div
|
||||
className={styles.tag}
|
||||
>
|
||||
<TagInputTag
|
||||
@@ -22,7 +22,7 @@ function FilterBuilderRowValueTag(props) {
|
||||
{translate('Or')}
|
||||
</div>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-right: $formLabelRightMarginWidth;
|
||||
padding-top: 8px;
|
||||
min-height: 35px;
|
||||
text-align: end;
|
||||
font-weight: bold;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
.hasError {
|
||||
|
||||
@@ -37,6 +37,8 @@ function getType({ type, selectOptionsProviderAction }) {
|
||||
return inputTypes.OAUTH;
|
||||
case 'rootFolder':
|
||||
return inputTypes.ROOT_FOLDER_SELECT;
|
||||
case 'qualityProfile':
|
||||
return inputTypes.QUALITY_PROFILE_SELECT;
|
||||
default:
|
||||
return inputTypes.TEXT;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import Link from 'Components/Link/Link';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ModalContent.css';
|
||||
|
||||
function ModalContent(props) {
|
||||
@@ -28,6 +29,7 @@ function ModalContent(props) {
|
||||
<Icon
|
||||
name={icons.CLOSE}
|
||||
size={18}
|
||||
title={translate('Close')}
|
||||
/>
|
||||
</Link>
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ class PageHeader extends Component {
|
||||
aria-label="Donate"
|
||||
to="https://radarr.video/donate"
|
||||
size={14}
|
||||
title={translate('Donate')}
|
||||
/>
|
||||
<IconButton
|
||||
className={styles.translate}
|
||||
|
||||
@@ -24,6 +24,7 @@ function PageHeaderActionsMenu(props) {
|
||||
<MenuButton className={styles.menuButton} aria-label="Menu Button">
|
||||
<Icon
|
||||
name={icons.INTERACTIVE}
|
||||
title={translate('Menu')}
|
||||
/>
|
||||
</MenuButton>
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ import MovieCollectionLabelConnector from './../MovieCollectionLabelConnector';
|
||||
import MovieCastPostersConnector from './Credits/Cast/MovieCastPostersConnector';
|
||||
import MovieCrewPostersConnector from './Credits/Crew/MovieCrewPostersConnector';
|
||||
import MovieDetailsLinks from './MovieDetailsLinks';
|
||||
import MovieReleaseDatesConnector from './MovieReleaseDatesConnector';
|
||||
import MovieReleaseDates from './MovieReleaseDates';
|
||||
import MovieStatusLabel from './MovieStatusLabel';
|
||||
import MovieTagsConnector from './MovieTagsConnector';
|
||||
import MovieTitlesTable from './Titles/MovieTitlesTable';
|
||||
@@ -433,7 +433,7 @@ class MovieDetails extends Component {
|
||||
}
|
||||
title={translate('ReleaseDates')}
|
||||
body={
|
||||
<MovieReleaseDatesConnector
|
||||
<MovieReleaseDates
|
||||
inCinemas={inCinemas}
|
||||
physicalRelease={physicalRelease}
|
||||
digitalRelease={digitalRelease}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||
import styles from './MovieReleaseDates.css';
|
||||
|
||||
function MovieReleaseDates(props) {
|
||||
const {
|
||||
showRelativeDates,
|
||||
shortDateFormat,
|
||||
timeFormat,
|
||||
inCinemas,
|
||||
physicalRelease,
|
||||
digitalRelease
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
!!inCinemas &&
|
||||
<div >
|
||||
<div className={styles.dateIcon}>
|
||||
<Icon
|
||||
name={icons.IN_CINEMAS}
|
||||
/>
|
||||
</div>
|
||||
{getRelativeDate(inCinemas, shortDateFormat, showRelativeDates, { timeFormat, timeForToday: false })}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
!!digitalRelease &&
|
||||
<div >
|
||||
<div className={styles.dateIcon}>
|
||||
<Icon
|
||||
name={icons.MOVIE_FILE}
|
||||
/>
|
||||
</div>
|
||||
{getRelativeDate(digitalRelease, shortDateFormat, showRelativeDates, { timeFormat, timeForToday: false })}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
!!physicalRelease &&
|
||||
<div >
|
||||
<div className={styles.dateIcon}>
|
||||
<Icon
|
||||
name={icons.DISC}
|
||||
/>
|
||||
</div>
|
||||
{getRelativeDate(physicalRelease, shortDateFormat, showRelativeDates, { timeFormat, timeForToday: false })}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
MovieReleaseDates.propTypes = {
|
||||
showRelativeDates: PropTypes.bool.isRequired,
|
||||
shortDateFormat: PropTypes.string.isRequired,
|
||||
longDateFormat: PropTypes.string.isRequired,
|
||||
timeFormat: PropTypes.string.isRequired,
|
||||
inCinemas: PropTypes.string,
|
||||
physicalRelease: PropTypes.string,
|
||||
digitalRelease: PropTypes.string
|
||||
};
|
||||
|
||||
export default MovieReleaseDates;
|
||||
64
frontend/src/Movie/Details/MovieReleaseDates.tsx
Normal file
64
frontend/src/Movie/Details/MovieReleaseDates.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Icon from 'Components/Icon';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './MovieReleaseDates.css';
|
||||
|
||||
interface MovieReleaseDatesProps {
|
||||
inCinemas: string;
|
||||
physicalRelease: string;
|
||||
digitalRelease: string;
|
||||
}
|
||||
|
||||
function MovieReleaseDates(props: MovieReleaseDatesProps) {
|
||||
const { inCinemas, physicalRelease, digitalRelease } = props;
|
||||
|
||||
const { showRelativeDates, shortDateFormat, timeFormat } = useSelector(
|
||||
createUISettingsSelector()
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{inCinemas ? (
|
||||
<div title={translate('InCinemas')}>
|
||||
<div className={styles.dateIcon}>
|
||||
<Icon name={icons.IN_CINEMAS} />
|
||||
</div>
|
||||
{getRelativeDate(inCinemas, shortDateFormat, showRelativeDates, {
|
||||
timeFormat,
|
||||
timeForToday: false,
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
{digitalRelease ? (
|
||||
<div title={translate('DigitalRelease')}>
|
||||
<div className={styles.dateIcon}>
|
||||
<Icon name={icons.MOVIE_FILE} />
|
||||
</div>
|
||||
{getRelativeDate(digitalRelease, shortDateFormat, showRelativeDates, {
|
||||
timeFormat,
|
||||
timeForToday: false,
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
{physicalRelease ? (
|
||||
<div title={translate('PhysicalRelease')}>
|
||||
<div className={styles.dateIcon}>
|
||||
<Icon name={icons.DISC} />
|
||||
</div>
|
||||
{getRelativeDate(
|
||||
physicalRelease,
|
||||
shortDateFormat,
|
||||
showRelativeDates,
|
||||
{ timeFormat, timeForToday: false }
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MovieReleaseDates;
|
||||
@@ -1,20 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import MovieReleaseDates from './MovieReleaseDates';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createUISettingsSelector(),
|
||||
(uiSettings) => {
|
||||
return {
|
||||
showRelativeDates: uiSettings.showRelativeDates,
|
||||
shortDateFormat: uiSettings.shortDateFormat,
|
||||
longDateFormat: uiSettings.longDateFormat,
|
||||
timeFormat: uiSettings.timeFormat
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(createMapStateToProps, null)(MovieReleaseDates);
|
||||
@@ -100,6 +100,15 @@ function MovieIndexSortMenu(props: MovieIndexSortMenuProps) {
|
||||
{translate('DigitalRelease')}
|
||||
</SortMenuItem>
|
||||
|
||||
<SortMenuItem
|
||||
name="releaseDate"
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
onPress={onSortSelect}
|
||||
>
|
||||
{translate('ReleaseDates')}
|
||||
</SortMenuItem>
|
||||
|
||||
<SortMenuItem
|
||||
name="tmdbRating"
|
||||
sortKey={sortKey}
|
||||
|
||||
@@ -80,8 +80,12 @@ function DownloadClientOptions(props) {
|
||||
legend={translate('FailedDownloadHandling')}
|
||||
>
|
||||
<Form>
|
||||
<FormGroup size={sizes.MEDIUM}>
|
||||
<FormLabel>{translate('Redownload')}</FormLabel>
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('AutoRedownloadFailed')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
@@ -91,7 +95,28 @@ function DownloadClientOptions(props) {
|
||||
{...settings.autoRedownloadFailed}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{
|
||||
settings.autoRedownloadFailed.value ?
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>{translate('AutoRedownloadFailedFromInteractiveSearch')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="autoRedownloadFailedFromInteractiveSearch"
|
||||
helpText={translate('AutoRedownloadFailedFromInteractiveSearchHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.autoRedownloadFailedFromInteractiveSearch}
|
||||
/>
|
||||
</FormGroup> :
|
||||
null
|
||||
}
|
||||
</Form>
|
||||
|
||||
<Alert kind={kinds.INFO}>
|
||||
{translate('RemoveDownloadsAlert')}
|
||||
</Alert>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { createAction } from 'redux-actions';
|
||||
import { batchActions } from 'redux-batched-actions';
|
||||
import { filterTypePredicates, filterTypes, sortDirections } from 'Helpers/Props';
|
||||
@@ -240,16 +241,55 @@ export const sortPredicates = {
|
||||
return item.year || undefined;
|
||||
},
|
||||
|
||||
inCinemas: function(item) {
|
||||
return item.inCinemas || '';
|
||||
inCinemas: function(item, direction) {
|
||||
if (item.inCinemas) {
|
||||
return moment(item.inCinemas).unix();
|
||||
}
|
||||
|
||||
if (direction === sortDirections.DESCENDING) {
|
||||
return -1 * Number.MAX_VALUE;
|
||||
}
|
||||
|
||||
return Number.MAX_VALUE;
|
||||
},
|
||||
|
||||
physicalRelease: function(item) {
|
||||
return item.physicalRelease || '';
|
||||
physicalRelease: function(item, direction) {
|
||||
if (item.physicalRelease) {
|
||||
return moment(item.physicalRelease).unix();
|
||||
}
|
||||
|
||||
if (direction === sortDirections.DESCENDING) {
|
||||
return -1 * Number.MAX_VALUE;
|
||||
}
|
||||
|
||||
return Number.MAX_VALUE;
|
||||
},
|
||||
|
||||
digitalRelease: function(item) {
|
||||
return item.digitalRelease || '';
|
||||
digitalRelease: function(item, direction) {
|
||||
if (item.digitalRelease) {
|
||||
return moment(item.digitalRelease).unix();
|
||||
}
|
||||
|
||||
if (direction === sortDirections.DESCENDING) {
|
||||
return -1 * Number.MAX_VALUE;
|
||||
}
|
||||
|
||||
return Number.MAX_VALUE;
|
||||
},
|
||||
|
||||
releaseDate: function(item, direction) {
|
||||
const { inCinemas, digitalRelease, physicalRelease } = item;
|
||||
const releaseDate = digitalRelease || physicalRelease || inCinemas;
|
||||
|
||||
if (releaseDate) {
|
||||
return moment(releaseDate).unix();
|
||||
}
|
||||
|
||||
if (direction === sortDirections.DESCENDING) {
|
||||
return -1 * Number.MAX_VALUE;
|
||||
}
|
||||
|
||||
return Number.MAX_VALUE;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
function createCollectionSelector() {
|
||||
return createSelector(
|
||||
(state, { collectionId }) => collectionId,
|
||||
(state) => state.movieCollections.itemMap,
|
||||
(state) => state.movieCollections.items,
|
||||
(collectionId, itemMap, allCollections) => {
|
||||
if (allCollections && itemMap && collectionId in itemMap) {
|
||||
return allCollections[itemMap[collectionId]];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createCollectionSelector;
|
||||
17
frontend/src/Store/Selectors/createCollectionSelector.ts
Normal file
17
frontend/src/Store/Selectors/createCollectionSelector.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import AppState from 'App/State/AppState';
|
||||
|
||||
function createCollectionSelector() {
|
||||
return createSelector(
|
||||
(_: AppState, { collectionId }: { collectionId: number }) => collectionId,
|
||||
(state: AppState) => state.movieCollections.itemMap,
|
||||
(state: AppState) => state.movieCollections.items,
|
||||
(collectionId, itemMap, allCollections) => {
|
||||
return allCollections && itemMap && collectionId in itemMap
|
||||
? allCollections[itemMap[collectionId]]
|
||||
: undefined;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createCollectionSelector;
|
||||
@@ -72,6 +72,7 @@ function getInternalLink(source) {
|
||||
function getTestLink(source, props) {
|
||||
switch (source) {
|
||||
case 'IndexerStatusCheck':
|
||||
case 'IndexerLongTermStatusCheck':
|
||||
return (
|
||||
<SpinnerIconButton
|
||||
name={icons.TEST}
|
||||
|
||||
@@ -73,15 +73,15 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
|
||||
|
||||
// Announce URLs (passkeys) Magnet & Tracker
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2ftracker.php%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce%2f9pr04sg601233210imaveql2tyu8xyui""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce.php%3fpasskey%3d9pr04sg601233210imaveql2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/9pr04sg601233210imaveql2tyu8xyui/announce""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/tracker.php/9pr04sg601233210imaveql2tyu8xyui/announce""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/announce/9pr04sg601233210imaveql2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""http://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui"",""info"":""http://xxx.yyy/info?a=b""")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210IMAveQL2tyu8xyui%2fannounce""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2ftracker.php%2f9pr04sg601233210IMAveQL2tyu8xyui%2fannounce""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce%2f9pr04sg601233210IMAveQL2tyu8xyui""}")]
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce.php%3fpasskey%3d9pr04sg601233210IMAveQL2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/9pr04sg601233210IMAveQL2tyu8xyui/announce""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/tracker.php/9pr04sg601233210IMAveQL2tyu8xyui/announce""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/announce/9pr04sg601233210IMAveQL2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""https://xxx.yyy/announce.php?passkey=9pr04sg601233210IMAveQL2tyu8xyui""}")]
|
||||
[TestCase(@"tracker"":""http://xxx.yyy/announce.php?passkey=9pr04sg601233210IMAveQL2tyu8xyui"",""info"":""http://xxx.yyy/info?a=b""")]
|
||||
|
||||
// Notifiarr
|
||||
[TestCase(@"https://xxx.yyy/api/v1/notification/radarr/9pr04sg6-0123-3210-imav-eql2tyu8xyui")]
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
new (@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
|
||||
new (@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"),
|
||||
new (@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Path
|
||||
new (@"C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
public static class NzbDroneLogger
|
||||
{
|
||||
private const string FILE_LOG_LAYOUT = @"${date:format=yyyy-MM-dd HH\:mm\:ss.fff}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
private const string FILE_LOG_LAYOUT = @"${date:format=yyyy-MM-dd HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}";
|
||||
|
||||
private static bool _isConfigured;
|
||||
|
||||
|
||||
@@ -72,11 +72,15 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("How the Earth Was Made S02 Disc 1 1080i Blu-ray DTS-HD MA 2.0 AVC-TrollHD")]
|
||||
[TestCase("The Universe S03 Disc 1 1080p Blu-ray LPCM 2.0 AVC-TrollHD")]
|
||||
[TestCase("HELL ON WHEELS S02 1080P FULL BLURAY AVC DTS-HD MA 5 1")]
|
||||
[TestCase("Game.of.Thrones.S06.2016.DISC.3.BluRay.1080p.AVC.Atmos.TrueHD7.1-MTeam")]
|
||||
[TestCase("Game of Thrones S05 Disc 1 BluRay 1080p AVC Atmos TrueHD 7 1-MTeam")]
|
||||
[TestCase("Series Title S02 Disc 1 1080i Blu-ray DTS-HD MA 2.0 AVC-TrollHD")]
|
||||
[TestCase("Series Title S03 Disc 1 1080p Blu-ray LPCM 2.0 AVC-TrollHD")]
|
||||
[TestCase("SERIES TITLE S02 1080P FULL BLURAY AVC DTS-HD MA 5 1")]
|
||||
[TestCase("Series.Title.S06.2016.DISC.3.BluRay.1080p.AVC.Atmos.TrueHD7.1-MTeam")]
|
||||
[TestCase("Series Title S05 Disc 1 BluRay 1080p AVC Atmos TrueHD 7 1-MTeam")]
|
||||
[TestCase("Series Title S05 Disc 1 BluRay 1080p AVC Atmos TrueHD 7 1-MTeam")]
|
||||
[TestCase("Someone.the.Entertainer.Presents.S01.NTSC.3xDVD9.MPEG-2.DD2.0")]
|
||||
[TestCase("Series.Title.S00.The.Christmas.Special.2011.PAL.DVD5.DD2.0")]
|
||||
[TestCase("Series.of.Desire.2000.S1_D01.NTSC.DVD5")]
|
||||
public void should_return_false_if_matches_disc_format(string title)
|
||||
{
|
||||
_remoteMovie.Release.Title = title;
|
||||
|
||||
@@ -364,6 +364,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("German.Only.Movie.2021.French.1080p.BluRay.AVC-UNTAVC")]
|
||||
[TestCase("Movie.Title.2008.US.Directors.Cut.UHD.BD66.Blu-ray")]
|
||||
[TestCase("Movie.2009.Blu.ray.AVC.DTS.HD.MA.5.1")]
|
||||
[TestCase("[BD]Movie.Title.2008.2023.1080p.COMPLETE.BLURAY-RlsGrp")]
|
||||
public void should_parse_brdisk_1080p_quality(string title)
|
||||
{
|
||||
ParseAndVerifyQuality(title, QualitySource.BLURAY, false, Resolution.R1080p, Modifier.BRDISK);
|
||||
|
||||
@@ -66,7 +66,8 @@ namespace NzbDrone.Core.Annotations
|
||||
OAuth,
|
||||
Device,
|
||||
TagSelect,
|
||||
RootFolder
|
||||
RootFolder,
|
||||
QualityProfile
|
||||
}
|
||||
|
||||
public enum HiddenType
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
{
|
||||
public class QualityProfileSpecificationValidator : AbstractValidator<QualityProfileSpecification>
|
||||
{
|
||||
public QualityProfileSpecificationValidator()
|
||||
{
|
||||
RuleFor(c => c.Value).GreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
public class QualityProfileSpecification : AutoTaggingSpecificationBase
|
||||
{
|
||||
private static readonly QualityProfileSpecificationValidator Validator = new ();
|
||||
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Quality Profile";
|
||||
|
||||
[FieldDefinition(1, Label = "Quality Profile", Type = FieldType.QualityProfile)]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Movie movie)
|
||||
{
|
||||
return Value == movie.QualityProfileId;
|
||||
}
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,6 +190,13 @@ namespace NzbDrone.Core.Configuration
|
||||
set { SetValue("AutoRedownloadFailed", value); }
|
||||
}
|
||||
|
||||
public bool AutoRedownloadFailedFromInteractiveSearch
|
||||
{
|
||||
get { return GetValueBoolean("AutoRedownloadFailedFromInteractiveSearch", true); }
|
||||
|
||||
set { SetValue("AutoRedownloadFailedFromInteractiveSearch", value); }
|
||||
}
|
||||
|
||||
public bool CreateEmptyMovieFolders
|
||||
{
|
||||
get { return GetValueBoolean("CreateEmptyMovieFolders", false); }
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace NzbDrone.Core.Configuration
|
||||
bool EnableCompletedDownloadHandling { get; set; }
|
||||
|
||||
bool AutoRedownloadFailed { get; set; }
|
||||
bool AutoRedownloadFailedFromInteractiveSearch { get; set; }
|
||||
|
||||
// Media Management
|
||||
bool AutoUnmonitorPreviouslyDownloadedMovies { get; set; }
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
private static readonly Regex[] DiscRegex = new[]
|
||||
{
|
||||
new Regex(@"(?:dis[ck])(?:[-_. ]\d+[-_. ])(?:(?:(?:480|720|1080|2160)[ip]|)[-_. ])?(?:Blu\-?ray)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?:(?:480|720|1080|2160)[ip]|)[-_. ](?:full)[-_. ](?:Blu\-?ray)", RegexOptions.Compiled | RegexOptions.IgnoreCase)
|
||||
new Regex(@"(?:(?:480|720|1080|2160)[ip]|)[-_. ](?:full)[-_. ](?:Blu\-?ray)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?:\d?x?M?DVD-?[R59])", RegexOptions.Compiled | RegexOptions.IgnoreCase)
|
||||
};
|
||||
|
||||
private static readonly string[] _dvdContainerTypes = new[] { "vob", "iso" };
|
||||
@@ -39,8 +40,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
{
|
||||
if (regex.IsMatch(subject.Release.Title))
|
||||
{
|
||||
_logger.Debug("Release contains raw Bluray, rejecting.");
|
||||
return Decision.Reject("Raw Bluray release");
|
||||
_logger.Debug("Release contains raw Bluray/DVD, rejecting.");
|
||||
return Decision.Reject("Raw Bluray/DVD release");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
@@ -23,5 +24,6 @@ namespace NzbDrone.Core.Download
|
||||
public TrackedDownload TrackedDownload { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public bool SkipRedownload { get; set; }
|
||||
public ReleaseSourceType ReleaseSource { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
@@ -128,6 +130,7 @@ namespace NzbDrone.Core.Download
|
||||
private void PublishDownloadFailedEvent(List<MovieHistory> historyItems, string message, TrackedDownload trackedDownload = null, bool skipRedownload = false)
|
||||
{
|
||||
var historyItem = historyItems.First();
|
||||
Enum.TryParse(historyItem.Data.GetValueOrDefault(MovieHistory.RELEASE_SOURCE, ReleaseSourceType.Unknown.ToString()), out ReleaseSourceType releaseSource);
|
||||
|
||||
var downloadFailedEvent = new DownloadFailedEvent
|
||||
{
|
||||
@@ -140,7 +143,8 @@ namespace NzbDrone.Core.Download
|
||||
Data = historyItem.Data,
|
||||
TrackedDownload = trackedDownload,
|
||||
Languages = historyItem.Languages,
|
||||
SkipRedownload = skipRedownload
|
||||
SkipRedownload = skipRedownload,
|
||||
ReleaseSource = releaseSource
|
||||
};
|
||||
|
||||
_eventAggregator.PublishEvent(downloadFailedEvent);
|
||||
|
||||
@@ -5,6 +5,7 @@ using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.Messaging;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
@@ -38,6 +39,12 @@ namespace NzbDrone.Core.Download
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.ReleaseSource == ReleaseSourceType.InteractiveSearch && !_configService.AutoRedownloadFailedFromInteractiveSearch)
|
||||
{
|
||||
_logger.Debug("Auto redownloading failed movies from interactive search is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.MovieId != 0)
|
||||
{
|
||||
_logger.Debug("Failed download contains a movie, searching again.");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Messaging;
|
||||
@@ -28,6 +29,7 @@ namespace NzbDrone.Core.HealthCheck
|
||||
private readonly IProvideHealthCheck[] _scheduledHealthChecks;
|
||||
private readonly Dictionary<Type, IEventDrivenHealthCheck[]> _eventDrivenHealthChecks;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private readonly ICached<HealthCheck> _healthCheckResults;
|
||||
private readonly HashSet<IProvideHealthCheck> _pendingHealthChecks;
|
||||
@@ -40,10 +42,12 @@ namespace NzbDrone.Core.HealthCheck
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheManager cacheManager,
|
||||
IDebounceManager debounceManager,
|
||||
IRuntimeInfo runtimeInfo)
|
||||
IRuntimeInfo runtimeInfo,
|
||||
Logger logger)
|
||||
{
|
||||
_healthChecks = healthChecks.ToArray();
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
|
||||
_healthCheckResults = cacheManager.GetCache<HealthCheck>(GetType());
|
||||
_pendingHealthChecks = new HashSet<IProvideHealthCheck>();
|
||||
@@ -88,7 +92,14 @@ namespace NzbDrone.Core.HealthCheck
|
||||
|
||||
try
|
||||
{
|
||||
var results = healthChecks.Select(c => c.Check())
|
||||
var results = healthChecks.Select(c =>
|
||||
{
|
||||
_logger.Trace("Check health -> {0}", c.GetType().Name);
|
||||
var result = c.Check();
|
||||
_logger.Trace("Check health <- {0}", c.GetType().Name);
|
||||
|
||||
return result;
|
||||
})
|
||||
.ToList();
|
||||
|
||||
foreach (var result in results)
|
||||
|
||||
@@ -43,9 +43,9 @@ namespace NzbDrone.Core.ImportLists
|
||||
|
||||
private void SyncAll()
|
||||
{
|
||||
if (_importListFactory.Enabled().Where(a => ((ImportListDefinition)a.Definition).EnableAuto).Empty())
|
||||
if (_importListFactory.Enabled().Empty())
|
||||
{
|
||||
_logger.Debug("No import lists with automatic add enabled, skipping sync and cleaning");
|
||||
_logger.Debug("No enabled import lists, skipping sync and cleaning");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,6 @@
|
||||
"RefreshInformationAndScanDisk": "تحديث المعلومات ومسح القرص",
|
||||
"RefreshAndScan": "التحديث والمسح الضوئي",
|
||||
"Refresh": "تحديث",
|
||||
"Redownload": "إعادة التنزيل",
|
||||
"RecyclingBinCleanup": "تنظيف سلة إعادة التدوير",
|
||||
"RecyclingBin": "صندوق إعادة التدوير",
|
||||
"RecycleBinHelpText": "ستنتقل ملفات الأفلام إلى هنا عند حذفها بدلاً من حذفها نهائيًا",
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
"ReadTheWikiForMoreInformation": "Прочетете Wiki за повече информация",
|
||||
"Reason": "Причина",
|
||||
"RecyclingBinCleanup": "Почистване на кошчето за рециклиране",
|
||||
"Redownload": "Презареждане",
|
||||
"Refresh": "Обнови",
|
||||
"RefreshMovie": "Опресняване на филма",
|
||||
"RegularExpressionsCanBeTested": "Регулярните изрази могат да бъдат тествани ",
|
||||
|
||||
@@ -730,7 +730,6 @@
|
||||
"RecycleBinCleanupDaysHelpTextWarning": "Els fitxers de la paperera de reciclatge més antics que el nombre de dies seleccionat es netejaran automàticament",
|
||||
"RecycleBinHelpText": "Els fitxers de pel·lícula aniran aquí quan se suprimeixin en lloc de suprimir-se permanentment",
|
||||
"RecyclingBinCleanup": "Neteja de la paperera de reciclatge",
|
||||
"Redownload": "Torna a baixar",
|
||||
"RefreshCollections": "Actualitza col·leccions",
|
||||
"RefreshInformationAndScanDisk": "Actualitza la informació i escaneja el disc",
|
||||
"RefreshLists": "Actualitza llistes",
|
||||
|
||||
@@ -315,7 +315,6 @@
|
||||
"ReadTheWikiForMoreInformation": "Další informace najdete na Wiki",
|
||||
"Reason": "Důvod",
|
||||
"RecyclingBinCleanup": "Recyklace koše Vyčištění",
|
||||
"Redownload": "Znovu stáhnout",
|
||||
"Refresh": "Obnovit",
|
||||
"RefreshMovie": "Obnovit film",
|
||||
"RegularExpressionsCanBeTested": "Regulární výrazy lze testovat ",
|
||||
|
||||
@@ -313,7 +313,6 @@
|
||||
"ReadTheWikiForMoreInformation": "Læs Wiki for mere information",
|
||||
"Reason": "Grund",
|
||||
"RecyclingBinCleanup": "Oprydning af papirkurven",
|
||||
"Redownload": "Genindlæs",
|
||||
"Refresh": "Opdater",
|
||||
"RefreshMovie": "Opdater film",
|
||||
"RegularExpressionsCanBeTested": "Regulære udtryk kan testes ",
|
||||
|
||||
@@ -461,7 +461,6 @@
|
||||
"RecycleBinHelpText": "Gelöschte Filmdateien werden hierher verschoben anstatt sie direkt endgültig zu löschen",
|
||||
"RecyclingBin": "Papierkorb",
|
||||
"RecyclingBinCleanup": "Papierkorb aufräumen",
|
||||
"Redownload": "Nochmal herunterladen",
|
||||
"RefreshInformationAndScanDisk": "Metadaten aktualisieren und Festplatte scannen",
|
||||
"RefreshMovie": "Film aktualisieren",
|
||||
"ReleaseRejected": "Release abgelehnt",
|
||||
@@ -960,7 +959,7 @@
|
||||
"AddRootFolder": "Stammordner hinzufügen",
|
||||
"AddQualityProfile": "Qualitätsprofil hinzufügen",
|
||||
"AddedToDownloadQueue": "Zur Downloadwarteschlange hinzugefügt",
|
||||
"AddDownloadClient": "Zum Downloader hinzufügen",
|
||||
"AddDownloadClient": "Downloadmanager hinzufügen",
|
||||
"AddCustomFormat": "Eigenes Format hinzufügen",
|
||||
"AddDelayProfile": "Verzögerungsprofil hinzufügen",
|
||||
"Add": "Hinzufügen",
|
||||
@@ -1170,12 +1169,14 @@
|
||||
"ApplyTagsHelpTextHowToApplyImportLists": "Wie werden Tags zu ausgewählten Filmen zugeteilt",
|
||||
"DeleteRootFolderMessageText": "Indexer '{0}' wirklich löschen?",
|
||||
"DeleteRootFolder": "Stammordner löschen",
|
||||
"AddConnection": "Sammlung bearbeiten",
|
||||
"AddConnection": "Verbindung hinzufügen",
|
||||
"BypassDelayIfAboveCustomFormatScoreMinimumScore": "Minimum der eigenen Formate Bewertungspunkte",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Wegen Fehlern sind keine Applikationen verfügbar",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Applikationen wegen folgender Fehler nicht verfügbar: {0}",
|
||||
"RemoveFromDownloadClientHelpTextWarning": "Dies wird den Download und alle bereits heruntergeladenen Dateien aus dem Downloader entfernen.",
|
||||
"DownloadClientsLoadError": "Downloader konnten nicht geladen werden",
|
||||
"IMDbId": "TMDb ID",
|
||||
"DisabledForLocalAddresses": "Für Lokale Adressen deaktivieren"
|
||||
"DisabledForLocalAddresses": "Für Lokale Adressen deaktivieren",
|
||||
"AddCondition": "Bedingung hinzufügen",
|
||||
"AddAutoTag": "Automatisches Tag hinzufügen"
|
||||
}
|
||||
|
||||
@@ -197,7 +197,6 @@
|
||||
"AutomaticSearch": "Αυτόματη αναζήτηση",
|
||||
"ChmodGroup": "Ομάδα chmod",
|
||||
"Queued": "Σε ουρά",
|
||||
"Redownload": "Κατεβάστε ξανά",
|
||||
"Refresh": "Φρεσκάρω",
|
||||
"RelativePath": "Σχετική διαδρομή",
|
||||
"Released": "Κυκλοφόρησε",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"AuthenticationRequiredUsernameHelpTextWarning": "Enter a new username",
|
||||
"AuthenticationRequiredWarning": "To prevent remote access without authentication, {appName} now requires authentication to be enabled. You can optionally disable authentication from local addresses.",
|
||||
"Auto": "Auto",
|
||||
"AutoRedownloadFailed": "Redownload Failed",
|
||||
"AutoRedownloadFailedFromInteractiveSearch": "Redownload Failed from Interactive Search",
|
||||
"AutoRedownloadFailedFromInteractiveSearchHelpText": "Automatically search for and attempt to download a different release when failed release was grabbed from interactive search",
|
||||
"AutoRedownloadFailedHelpText": "Automatically search for and attempt to download a different release",
|
||||
"AutoTagging": "Auto Tagging",
|
||||
"AutoTaggingNegateHelpText": "If checked, the auto tagging rule will not apply if this {0} condition matches.",
|
||||
@@ -908,7 +911,6 @@
|
||||
"RecyclingBin": "Recycling Bin",
|
||||
"RecyclingBinCleanup": "Recycling Bin Cleanup",
|
||||
"Reddit": "Reddit",
|
||||
"Redownload": "Redownload",
|
||||
"Refresh": "Refresh",
|
||||
"RefreshAndScan": "Refresh & Scan",
|
||||
"RefreshCollections": "Refresh Collections",
|
||||
|
||||
@@ -479,7 +479,6 @@
|
||||
"ReleaseDates": "Fechas de Estreno",
|
||||
"RefreshMovie": "Actualizar película",
|
||||
"RefreshInformationAndScanDisk": "Actualizar la información al escanear el disco",
|
||||
"Redownload": "Volver a descargar",
|
||||
"RecyclingBinCleanup": "Limpieza de Papelera de Reciclaje",
|
||||
"RecyclingBin": "Papelera de Reciclaje",
|
||||
"RecycleBinHelpText": "Los archivos iran aquí una vez se hayan borrado en vez de ser borrados permanentemente",
|
||||
|
||||
@@ -324,7 +324,6 @@
|
||||
"AlternativeTitle": "Vaihtoehtoinen nimi",
|
||||
"Age": "Ikä",
|
||||
"RecyclingBinCleanup": "Roskakorin tyhjennys",
|
||||
"Redownload": "Lataa uudelleen",
|
||||
"RefreshMovie": "Päivitä elokuva",
|
||||
"RejectionCount": "Hylkäämisluku",
|
||||
"RelativePath": "Suhteellinen polku",
|
||||
|
||||
@@ -685,7 +685,6 @@
|
||||
"RegularExpressionsCanBeTested": "Les expressions régulières peuvent être testées ",
|
||||
"RefreshMovie": "Actualiser le film",
|
||||
"RefreshInformationAndScanDisk": "Actualiser les informations et analyser le disque",
|
||||
"Redownload": "Télécharger à nouveau",
|
||||
"RecyclingBinCleanup": "Nettoyage de la Corbeille",
|
||||
"RecyclingBin": "Corbeille",
|
||||
"RecycleBinHelpText": "Les fichiers vidéo iront ici lorsqu'ils seront supprimés au lieu d'être supprimés définitivement",
|
||||
@@ -932,7 +931,7 @@
|
||||
"Required": "Obligatoire",
|
||||
"RestartReloadNote": "Remarque : Radarr redémarrera et rechargera automatiquement l'interface utilisateur pendant le processus de restauration.",
|
||||
"RSS": "RSS",
|
||||
"Score": "But",
|
||||
"Score": "Score",
|
||||
"Script": "Scénario",
|
||||
"SearchCutoffUnmet": "Limite de recherche non satisfaite",
|
||||
"SearchMissing": "Recherche manquante",
|
||||
@@ -1243,5 +1242,30 @@
|
||||
"TablePageSizeHelpText": "Nombre d'éléments à afficher sur chaque page",
|
||||
"UnknownEventTooltip": "Événement inconnu",
|
||||
"AppUpdated": "{appName} mis à jour",
|
||||
"AppUpdatedVersion": "{appName} a été mis à jour vers la version `{version}`, pour profiter des derniers changements, vous devrez relancer {appName}"
|
||||
"AppUpdatedVersion": "{appName} a été mis à jour vers la version `{version}`, pour profiter des derniers changements, vous devrez relancer {appName}",
|
||||
"GrabId": "ID du grab",
|
||||
"InteractiveImportNoMovie": "Une langue doit être choisie pour chacun des fichiers sélectionnés",
|
||||
"InteractiveImportNoQuality": "Une langue doit être choisie pour chacun des fichiers sélectionnés",
|
||||
"UnableToLoadAutoTagging": "Impossible de charger le balisage automatique",
|
||||
"DelayingDownloadUntil": "Retarder le téléchargement jusqu'au {0} à {1}",
|
||||
"DeleteSelectedMovieFilesHelpText": "Voulez-vous vraiment supprimer les fichiers vidéo sélectionnés ?",
|
||||
"DeletedReasonMissingFromDisk": "Readarr n'a pas pu trouver le fichier sur le disque, il a donc été supprimé dans la base de données",
|
||||
"DeletedReasonUpgrade": "Le fichier à été supprimé pour importer une version supérieure",
|
||||
"OrganizeLoadError": "Erreur lors du chargement des aperçus",
|
||||
"EditImportListImplementation": "Ajouter une liste d'importation - {implementationName}",
|
||||
"EditIndexerImplementation": "Ajouter une condition - {implementationName}",
|
||||
"MovieFileDeleted": "À la suppression d'un fichier vidéo",
|
||||
"MovieFileDeletedTooltip": "À la suppression d'un fichier vidéo",
|
||||
"BlocklistReleaseHelpText": "Empêche Lidarr de récupérer automatiquement cette version",
|
||||
"InteractiveImportLoadError": "Impossible de charger les éléments d'importation manuelle",
|
||||
"MovieSearchResultsLoadError": "Impossible de charger les résultats de cette recherche de films. Réessayez plus tard",
|
||||
"QueueLoadError": "Erreur lors du chargement de la file",
|
||||
"RemoveSelectedBlocklistMessageText": "Êtes-vous sûr de vouloir supprimer les films sélectionnés de la liste noire ?",
|
||||
"RetryingDownloadOn": "Retarder le téléchargement jusqu'au {0} à {1}",
|
||||
"ShowUnknownMovieItemsHelpText": "Afficher les éléments sans film dans la file d'attente. Cela peut inclure des films supprimés ou tout autre élément de la catégorie de Lidarr",
|
||||
"TablePageSize": "Pagination",
|
||||
"EditConditionImplementation": "Ajouter une connexion - {implementationName}",
|
||||
"EditConnectionImplementation": "Ajouter une condition - {implementationName}",
|
||||
"ReleaseProfiles": "profil de version",
|
||||
"ReleaseProfilesLoadError": "Impossible de charger les profils de délai"
|
||||
}
|
||||
|
||||
@@ -227,7 +227,6 @@
|
||||
"ReadTheWikiForMoreInformation": "קרא את הוויקי למידע נוסף",
|
||||
"Reason": "סיבה",
|
||||
"RecyclingBinCleanup": "ניקוי סל המיחזור",
|
||||
"Redownload": "הורד מחדש",
|
||||
"Refresh": "לְרַעֲנֵן",
|
||||
"RefreshMovie": "רענן סרט",
|
||||
"RejectionCount": "ספירת דחייה",
|
||||
|
||||
@@ -442,7 +442,6 @@
|
||||
"Presets": "प्रीसेट",
|
||||
"Profiles": "प्रोफाइल",
|
||||
"Reason": "कारण",
|
||||
"Redownload": "redownload",
|
||||
"Refresh": "ताज़ा करना",
|
||||
"RefreshMovie": "फिल्म को रिफ्रेश करें",
|
||||
"Reset": "रीसेट",
|
||||
|
||||
@@ -392,7 +392,6 @@
|
||||
"RefreshInformationAndScanDisk": "Információk frissítése és lemez átvizsgálása",
|
||||
"RefreshAndScan": "Frissítés & Keresés",
|
||||
"Refresh": "Frissítés",
|
||||
"Redownload": "Letöltés újra",
|
||||
"RecyclingBinCleanup": "Lomtár kiürítése",
|
||||
"RecyclingBin": "Lomtár",
|
||||
"RecycleBinHelpText": "A filmfájlok végleges törlés helyett ide kerülnek törléskor",
|
||||
|
||||
@@ -289,7 +289,6 @@
|
||||
"RadarrCalendarFeed": "Radarr dagatalstraumur",
|
||||
"ReadTheWikiForMoreInformation": "Lestu Wiki fyrir frekari upplýsingar",
|
||||
"RecyclingBinCleanup": "Hreinsun ruslakörfu",
|
||||
"Redownload": "Endurhlaða",
|
||||
"Refresh": "Hressa",
|
||||
"RefreshMovie": "Hressa kvikmynd",
|
||||
"RelativePath": "Hlutfallsleg leið",
|
||||
|
||||
@@ -415,7 +415,6 @@
|
||||
"RegularExpressionsCanBeTested": "Le espressioni regolari possono essere testate ",
|
||||
"RefreshMovie": "Aggiorna il Film",
|
||||
"RefreshInformationAndScanDisk": "Aggiorna le informazioni e scansiona il disco",
|
||||
"Redownload": "Riscarica",
|
||||
"RecyclingBinCleanup": "Pulizia del cestino",
|
||||
"RecyclingBin": "Cestino",
|
||||
"RecycleBinHelpText": "I file dei film andranno qui quando cancellati invece che venire eliminati definitivamente",
|
||||
|
||||
@@ -268,7 +268,6 @@
|
||||
"ReadTheWikiForMoreInformation": "詳細については、Wikiをお読みください",
|
||||
"Reason": "理由",
|
||||
"RecyclingBinCleanup": "ごみ箱のクリーンアップ",
|
||||
"Redownload": "再ダウンロード",
|
||||
"Refresh": "更新",
|
||||
"RefreshMovie": "映画を更新する",
|
||||
"RegularExpressionsCanBeTested": "正規表現をテストできます ",
|
||||
|
||||
@@ -270,7 +270,6 @@
|
||||
"ReadTheWikiForMoreInformation": "자세한 내용은 Wiki를 참조하십시오.",
|
||||
"Reason": "이유",
|
||||
"RecyclingBinCleanup": "재활용 빈 정리",
|
||||
"Redownload": "다시 다운로드",
|
||||
"Refresh": "새롭게 하다",
|
||||
"RefreshMovie": "영화 새로 고침",
|
||||
"RegularExpressionsCanBeTested": "정규식을 테스트 할 수 있습니다. ",
|
||||
|
||||
@@ -512,7 +512,6 @@
|
||||
"RecycleBinCleanupDaysHelpTextWarning": "Bestanden in de prullenbak ouder dan het geselecteerde aantal dagen zullen automatisch opgeschoond worden",
|
||||
"RecyclingBin": "Prullenbak",
|
||||
"RecyclingBinCleanup": "Prullenbak Opruimen",
|
||||
"Redownload": "Opnieuw downloaden",
|
||||
"RefreshInformationAndScanDisk": "Informatie vernieuwen en schijf herscannen",
|
||||
"RefreshMovie": "Film vernieuwen",
|
||||
"ProxyType": "Proxy Type",
|
||||
|
||||
@@ -272,7 +272,6 @@
|
||||
"RadarrCalendarFeed": "Kanał kalendarza radarowego",
|
||||
"Reason": "Powód",
|
||||
"RecyclingBinCleanup": "Czyszczenie kosza na śmieci",
|
||||
"Redownload": "Pobierz ponownie",
|
||||
"Refresh": "Odświeżać",
|
||||
"RefreshMovie": "Odśwież film",
|
||||
"RegularExpressionsCanBeTested": "Można testować wyrażenia regularne ",
|
||||
|
||||
@@ -533,7 +533,6 @@
|
||||
"Remove": "Remover",
|
||||
"RefreshMovie": "Atualizar filme",
|
||||
"RefreshInformationAndScanDisk": "Atualizar informações e analisar o disco",
|
||||
"Redownload": "Transferir novamente",
|
||||
"RecyclingBinCleanup": "Limpeza da reciclagem",
|
||||
"RecyclingBin": "Reciclagem",
|
||||
"Reason": "Razão",
|
||||
|
||||
@@ -546,7 +546,6 @@
|
||||
"RetentionHelpText": "Somente Usenet: defina como zero para definir a retenção ilimitada",
|
||||
"RestartReloadNote": "Observação: o Radarr reiniciará automaticamente e recarregará a interface durante o processo de restauração.",
|
||||
"RejectionCount": "Número de rejeição",
|
||||
"Redownload": "Baixar novamente",
|
||||
"UpgradeAllowedHelpText": "Se desabilitada, as qualidades não serão atualizadas",
|
||||
"UpgradesAllowed": "Atualizações Permitidas",
|
||||
"UpgradeUntilCustomFormatScore": "Atualizar até pontuação de formato personalizado",
|
||||
@@ -1261,7 +1260,7 @@
|
||||
"True": "Verdadeiro",
|
||||
"HealthMessagesInfoBox": "Você pode encontrar mais informações sobre a causa dessas mensagens de verificação de integridade clicando no link da wiki (ícone do livro) no final da linha ou verificando seus [logs]({link}). Se tiver dificuldade em interpretar essas mensagens, você pode entrar em contato com nosso suporte, nos links abaixo.",
|
||||
"DefaultNameCopiedProfile": "{name} - Cópia",
|
||||
"InvalidUILanguage": "Sua IU está definida com um idioma inválido, corrija-a e salve suas configurações",
|
||||
"InvalidUILanguage": "Sua IU está configurada com um idioma inválido, corrija-a e salve suas configurações",
|
||||
"AuthenticationMethod": "Método de Autenticação",
|
||||
"AuthenticationMethodHelpTextWarning": "Selecione um método de autenticação válido",
|
||||
"AuthenticationRequiredPasswordHelpTextWarning": "Insira uma nova senha",
|
||||
|
||||
@@ -533,7 +533,6 @@
|
||||
"Local": "Local",
|
||||
"MovieYear": "Anul filmului",
|
||||
"RecyclingBinCleanup": "Curățarea coșului de reciclare",
|
||||
"Redownload": "Redescărcați",
|
||||
"RefreshMovie": "Reîmprospătați filmul",
|
||||
"MovieYearHelpText": "Anul filmului de exclus",
|
||||
"MustContain": "Trebuie sa contina",
|
||||
|
||||
@@ -367,7 +367,6 @@
|
||||
"RefreshInformationAndScanDisk": "Обновить информацию и просканировать диск",
|
||||
"RefreshAndScan": "Обновить & сканировать",
|
||||
"Refresh": "Обновить",
|
||||
"Redownload": "Перезакачать",
|
||||
"RecyclingBinCleanup": "Очистка мусорной корзины",
|
||||
"RecyclingBin": "Мусорная корзина",
|
||||
"RecycleBinHelpText": "Файлы фильмов будут попадать сюда при удалении",
|
||||
|
||||
@@ -437,7 +437,6 @@
|
||||
"RemoveMovieAndKeepFiles": "Ta bort film och behåll filerna",
|
||||
"RemoveMovieAndDeleteFiles": "Ta bort film och radera filerna",
|
||||
"RemoveFilter": "Ta bort filter",
|
||||
"Redownload": "Ladda ned igen",
|
||||
"RefreshLists": "Uppdatera listor",
|
||||
"RefreshInformationAndScanDisk": "Uppdatera information och skanna disken",
|
||||
"RecentFolders": "Senaste mappar",
|
||||
|
||||
@@ -387,7 +387,6 @@
|
||||
"QueueIsEmpty": "คิวว่างเปล่า",
|
||||
"RadarrCalendarFeed": "ฟีดปฏิทิน Radarr",
|
||||
"ReadTheWikiForMoreInformation": "อ่าน Wiki สำหรับข้อมูลเพิ่มเติม",
|
||||
"Redownload": "ดาวน์โหลดอีกครั้ง",
|
||||
"Refresh": "รีเฟรช",
|
||||
"RefreshMovie": "รีเฟรชภาพยนตร์",
|
||||
"RegularExpressionsCanBeTested": "นิพจน์ทั่วไปสามารถทดสอบได้ ",
|
||||
|
||||
@@ -591,7 +591,6 @@
|
||||
"PtpOldSettingsCheckMessage": "Aşağıdaki PassThePopcorn dizinleyicilerinin ayarları kullanımdan kaldırıldı ve güncellenmeleri gerekiyor: {0}",
|
||||
"Queued": "Sıraya alındı",
|
||||
"RecyclingBinCleanup": "Geri Dönüşüm Kutusu Temizleme",
|
||||
"Redownload": "Yeniden indir",
|
||||
"RefreshMovie": "Filmi yenile",
|
||||
"RejectionCount": "Reddetme Sayısı",
|
||||
"RelativePath": "Göreceli yol",
|
||||
|
||||
@@ -777,7 +777,6 @@
|
||||
"ReadTheWikiForMoreInformation": "Читайте Wiki для отримання додаткової інформації",
|
||||
"RecycleBinCleanupDaysHelpTextWarning": "Файли в кошику, старші за вибрану кількість днів, будуть очищені автоматично",
|
||||
"RecycleBinHelpText": "Файли фільмів потраплять сюди після видалення, а не назавжди",
|
||||
"Redownload": "Повторне завантаження",
|
||||
"RejectionCount": "Кількість відмов",
|
||||
"ReleaseBranchCheckOfficialBranchMessage": "Гілка {0} не є дійсною гілкою випуску Radarr, ви не отримуватимете оновлення",
|
||||
"ReleaseStatus": "Статус випуску",
|
||||
|
||||
@@ -343,7 +343,6 @@
|
||||
"RadarrCalendarFeed": "Nguồn cấp dữ liệu lịch Radarr",
|
||||
"ReadTheWikiForMoreInformation": "Đọc Wiki để biết thêm thông tin",
|
||||
"AptUpdater": "Sử dụng apt để cài đặt bản cập nhật",
|
||||
"Redownload": "Tải lại",
|
||||
"ReleasedMsg": "Phim được phát hành",
|
||||
"RecycleBinCleanupDaysHelpTextWarning": "Các tệp trong thùng rác cũ hơn số ngày đã chọn sẽ tự động được dọn dẹp",
|
||||
"AuthBasic": "Cơ bản (Cửa sổ bật lên trình duyệt)",
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
"BranchUpdate": "更新Radarr的分支",
|
||||
"Branch": "分支",
|
||||
"Calendar": "日历",
|
||||
"BackupRetentionHelpText": "早于保留周期的自动备份将被自动清除",
|
||||
"BackupRetentionHelpText": "超过保留期限的自动备份将被自动清理",
|
||||
"BackupNow": "马上备份",
|
||||
"BackupIntervalHelpText": "自动备份时间间隔",
|
||||
"BackupFolderHelpText": "相对路径将在Radarr的AppData目录下",
|
||||
@@ -98,7 +98,7 @@
|
||||
"AddNewMovie": "添加新电影",
|
||||
"AddRootFolder": "添加根目录",
|
||||
"AddQualityProfile": "添加质量配置",
|
||||
"AddNewTmdbIdMessage": "您也可以使用电影在TMDb的Id来搜索。例如 'tmdb:71663'",
|
||||
"AddNewTmdbIdMessage": "您还可以使用电影的TMDb Id进行搜索。例如 'tmdb:71663'",
|
||||
"UpdateAll": "全部更新",
|
||||
"AllMoviesHiddenDueToFilter": "根据应用的过滤项已隐藏全部的电影。",
|
||||
"AllFiles": "全部文件",
|
||||
@@ -699,7 +699,6 @@
|
||||
"QualitySettings": "媒体质量设置",
|
||||
"UseHardlinksInsteadOfCopy": "使用硬链接代替复制",
|
||||
"RecycleBinHelpText": "影片文件会被移动到回收站以替代永久删除",
|
||||
"Redownload": "重新下载",
|
||||
"TableOptions": "表格选项",
|
||||
"UpdateSelected": "更新已选",
|
||||
"ShowUnknownMovieItems": "显示未知影片条目",
|
||||
@@ -1213,7 +1212,7 @@
|
||||
"BypassDelayIfAboveCustomFormatScoreMinimumScoreHelpText": "绕过首选协议延迟所需的最小自定义格式分数",
|
||||
"ParseModalHelpTextDetails": "Radarr将尝试解析标题并向您显示有关它的详细信息",
|
||||
"AppUpdated": "{appName} 升级",
|
||||
"DelayingDownloadUntil": "将下载延迟到 {date} 的 {time}",
|
||||
"DelayingDownloadUntil": "将下载推迟到 {date} 的 {time}",
|
||||
"DeletedReasonManual": "文件已通过 UI 删除",
|
||||
"DeletedReasonUpgrade": "升级时删除原文件",
|
||||
"DownloadIgnored": "忽略下载",
|
||||
@@ -1237,7 +1236,7 @@
|
||||
"FullColorEventsHelpText": "改变样式,用状态颜色为整个事件着色,而不仅仅是左边缘。不适用于议程",
|
||||
"HistoryLoadError": "无法加载历史记录",
|
||||
"InfoUrl": "信息 URL",
|
||||
"InvalidUILanguage": "您的UI被设置为无效的语言,请纠正它并保存设置",
|
||||
"InvalidUILanguage": "您的UI设置的语言无效,请纠正它并保存设置",
|
||||
"LanguagesLoadError": "无法加载语言",
|
||||
"MovieDownloadFailedTooltip": "电影下载失败",
|
||||
"MovieDownloadIgnoredTooltip": "忽略电影下载",
|
||||
@@ -1255,11 +1254,11 @@
|
||||
"EditDownloadClientImplementation": "编辑下载客户端 - {implementationName}",
|
||||
"EditIndexerImplementation": "编辑索引器 - {implementationName}",
|
||||
"GrabId": "抓取ID",
|
||||
"HealthMessagesInfoBox": "您可以通过单击行尾的wiki链接(图书图标)或检查[logs]({link})来查找有关这些运行状况检查消息原因的更多信息。如果你在理解这些信息方面有困难,你可以通过下面的链接联系我们的支持。",
|
||||
"HealthMessagesInfoBox": "您可以通过单击行尾的wiki链接(图书图标)或检查[日志]({link})来查找有关这些运行状况检查消息原因的更多信息。如果你在理解这些信息方面有困难,你可以通过下面的链接联系我们的支持。",
|
||||
"IMDbId": "IMDb Id",
|
||||
"InteractiveImportNoMovie": "必须为每个选定的文件选择影片",
|
||||
"MovieFileDeletedTooltip": "删除电影文件",
|
||||
"MovieGrabbedHistoryTooltip": "从{indexer}抓取并发送到{downloadClient}的影片",
|
||||
"MovieGrabbedHistoryTooltip": "从{indexer}获取电影并发送到{downloadClient}",
|
||||
"IndexerDownloadClientHealthCheckMessage": "有无效下载客户端的索引器:{0}。",
|
||||
"FullColorEvents": "全彩事件",
|
||||
"InteractiveImportNoFilesFound": "在所选文件夹中没有找到视频文件",
|
||||
|
||||
@@ -2,9 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Movies.Translations;
|
||||
|
||||
namespace NzbDrone.Core.Movies
|
||||
{
|
||||
@@ -45,37 +43,7 @@ namespace NzbDrone.Core.Movies
|
||||
|
||||
public List<MovieMetadata> GetMoviesWithCollections()
|
||||
{
|
||||
var movieDictionary = new Dictionary<int, MovieMetadata>();
|
||||
|
||||
var builder = new SqlBuilder(_database.DatabaseType)
|
||||
.LeftJoin<MovieMetadata, MovieTranslation>((mm, t) => mm.Id == t.MovieMetadataId)
|
||||
.Where<MovieMetadata>(x => x.CollectionTmdbId > 0);
|
||||
|
||||
_ = _database.QueryJoined<MovieMetadata, MovieTranslation>(
|
||||
builder,
|
||||
(metadata, translation) =>
|
||||
{
|
||||
if (!movieDictionary.TryGetValue(metadata.Id, out var movieEntry))
|
||||
{
|
||||
movieEntry = metadata;
|
||||
movieDictionary.Add(movieEntry.Id, movieEntry);
|
||||
}
|
||||
|
||||
if (translation != null)
|
||||
{
|
||||
movieEntry.Translations.Add(translation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add a translation to avoid filename builder making another call thinking translations are not loaded
|
||||
// Optimize this later by pulling translations with metadata always
|
||||
movieEntry.Translations.Add(new MovieTranslation { Title = movieEntry.Title, Language = Language.English });
|
||||
}
|
||||
|
||||
return movieEntry;
|
||||
});
|
||||
|
||||
return movieDictionary.Values.ToList();
|
||||
return Query(x => x.CollectionTmdbId > 0);
|
||||
}
|
||||
|
||||
public List<MovieMetadata> GetMoviesByCollectionTmdbId(int collectionId)
|
||||
|
||||
@@ -50,17 +50,17 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
||||
switch ((int)responseCode)
|
||||
{
|
||||
case 401:
|
||||
_logger.Error("HTTP 401 - API key is invalid");
|
||||
_logger.Warn("HTTP 401 - API key is invalid");
|
||||
throw new NotifiarrException("API key is invalid");
|
||||
case 400:
|
||||
// 400 responses shouldn't be treated as an actual error because it's a misconfiguration
|
||||
// between Radarr and Notifiarr for a specific event, but shouldn't stop all events.
|
||||
_logger.Error("HTTP 400 - Unable to send notification. Ensure Radarr Integration is enabled & assigned a channel on Notifiarr");
|
||||
_logger.Warn("HTTP 400 - Unable to send notification. Ensure Radarr Integration is enabled & assigned a channel on Notifiarr");
|
||||
break;
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
_logger.Error("Unable to send notification. Service Unavailable");
|
||||
_logger.Warn("Unable to send notification. Service Unavailable");
|
||||
throw new NotifiarrException("Unable to send notification. Service Unavailable", ex);
|
||||
case 520:
|
||||
case 521:
|
||||
|
||||
@@ -250,7 +250,7 @@ namespace NzbDrone.Core.Organizer
|
||||
tokenHandlers["{Movie TitleThe}"] = m => TitleThe(movie.Title);
|
||||
tokenHandlers["{Movie TitleFirstCharacter}"] = m => TitleThe(GetLanguageTitle(movie, m.CustomFormat)).Substring(0, 1).FirstCharToUpper();
|
||||
tokenHandlers["{Movie OriginalTitle}"] = m => movie.MovieMetadata.Value.OriginalTitle ?? string.Empty;
|
||||
tokenHandlers["{Movie CleanOriginalTitle}"] = m => CleanTitle(movie.MovieMetadata.Value.OriginalTitle) ?? string.Empty;
|
||||
tokenHandlers["{Movie CleanOriginalTitle}"] = m => CleanTitle(movie.MovieMetadata.Value.OriginalTitle ?? string.Empty);
|
||||
|
||||
tokenHandlers["{Movie Certification}"] = m => movie.MovieMetadata.Value.Certification ?? string.Empty;
|
||||
tokenHandlers["{Movie Collection}"] = m => movie.MovieMetadata.Value.CollectionTitle ?? string.Empty;
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace NzbDrone.Core.Parser
|
||||
|
||||
private static readonly Regex MPEG2Regex = new (@"\b(?<mpeg2>MPEG[-_. ]?2)\b");
|
||||
|
||||
private static readonly Regex BRDISKRegex = new (@"^(?!.*\b((?<!HD[._ -]|HD)DVD|BDRip|720p|MKV|XviD|WMV|d3g|(BD)?REMUX|^(?=.*1080p)(?=.*HEVC)|[xh][-_. ]?26[45]|German.*[DM]L|((?<=\d{4}).*German.*([DM]L)?)(?=.*\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2)\b))\b)(((?=.*\b(Blu[-_. ]?ray|BD|HD[-_. ]?DVD)\b)(?=.*\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2|BDMV|ISO)\b))|^((?=.*\b(^((?=.*\b((.*_)?COMPLETE.*|Dis[ck])\b)(?=.*(Blu[-_. ]?ray|HD[-_. ]?DVD)))|3D[-_. ]?BD|BR[-_. ]?DISK|Full[-_. ]?Blu[-_. ]?ray|^((?=.*((BD|UHD)[-_. ]?(25|50|66|100|ISO)))))))).*",
|
||||
private static readonly Regex BRDISKRegex = new (@"^(?!.*\b((?<!HD[._ -]|HD)DVD|BDRip|720p|MKV|XviD|WMV|d3g|(BD)?REMUX|^(?=.*1080p)(?=.*HEVC)|[xh][-_. ]?26[45]|German.*[DM]L|((?<=\d{4}).*German.*([DM]L)?)(?=.*\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2)\b))\b)(((?=.*\b(Blu[-_. ]?ray|BD|HD[-_. ]?DVD)\b)(?=.*\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2|BDMV|ISO)\b))|^((?=.*\b(((?=.*\b((.*_)?COMPLETE.*|Dis[ck])\b)(?=.*(Blu[-_. ]?ray|HD[-_. ]?DVD)))|3D[-_. ]?BD|BR[-_. ]?DISK|Full[-_. ]?Blu[-_. ]?ray|^((?=.*((BD|UHD)[-_. ]?(25|50|66|100|ISO)))))))).*",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex ProperRegex = new (@"\b(?<proper>proper)\b",
|
||||
|
||||
@@ -127,8 +127,16 @@ namespace NzbDrone.Core.RemotePathMappings
|
||||
return remotePath;
|
||||
}
|
||||
|
||||
var mappings = All();
|
||||
|
||||
if (mappings.Empty())
|
||||
{
|
||||
return remotePath;
|
||||
}
|
||||
|
||||
_logger.Trace("Evaluating remote path remote mappings for match to host [{0}] and remote path [{1}]", host, remotePath.FullPath);
|
||||
foreach (var mapping in All())
|
||||
|
||||
foreach (var mapping in mappings)
|
||||
{
|
||||
_logger.Trace("Checking configured remote path mapping: {0} - {1}", mapping.Host, mapping.RemotePath);
|
||||
if (host.Equals(mapping.Host, StringComparison.InvariantCultureIgnoreCase) && new OsPath(mapping.RemotePath).Contains(remotePath))
|
||||
@@ -150,8 +158,16 @@ namespace NzbDrone.Core.RemotePathMappings
|
||||
return localPath;
|
||||
}
|
||||
|
||||
var mappings = All();
|
||||
|
||||
if (mappings.Empty())
|
||||
{
|
||||
return localPath;
|
||||
}
|
||||
|
||||
_logger.Trace("Evaluating remote path local mappings for match to host [{0}] and local path [{1}]", host, localPath.FullPath);
|
||||
foreach (var mapping in All())
|
||||
|
||||
foreach (var mapping in mappings)
|
||||
{
|
||||
_logger.Trace("Checking configured remote path mapping {0} - {1}", mapping.Host, mapping.RemotePath);
|
||||
if (host.Equals(mapping.Host, StringComparison.InvariantCultureIgnoreCase) && new OsPath(mapping.LocalPath).Contains(localPath))
|
||||
|
||||
@@ -159,6 +159,8 @@ namespace NzbDrone.Host
|
||||
{
|
||||
{ apikeyQuery, Array.Empty<string>() }
|
||||
});
|
||||
|
||||
c.DescribeAllParametersInCamelCase();
|
||||
});
|
||||
|
||||
services
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace NzbDrone.Integration.Test.Client
|
||||
{
|
||||
// cache control header gets reordered on net core
|
||||
var headers = response.Headers;
|
||||
((string)headers.Single(c => c.Name == "Cache-Control").Value).Split(',').Select(x => x.Trim())
|
||||
((string)headers.SingleOrDefault(c => c.Name == "Cache-Control")?.Value ?? string.Empty).Split(',').Select(x => x.Trim())
|
||||
.Should().BeEquivalentTo("no-store, no-cache".Split(',').Select(x => x.Trim()));
|
||||
headers.Single(c => c.Name == "Pragma").Value.Should().Be("no-cache");
|
||||
headers.Single(c => c.Name == "Expires").Value.Should().Be("-1");
|
||||
@@ -102,7 +102,7 @@ namespace NzbDrone.Integration.Test.Client
|
||||
return Get<List<TResource>>(request);
|
||||
}
|
||||
|
||||
public PagingResource<TResource> GetPaged(int pageNumber, int pageSize, string sortKey, string sortDir, string filterKey = null, string filterValue = null)
|
||||
public PagingResource<TResource> GetPaged(int pageNumber, int pageSize, string sortKey, string sortDir, string filterKey = null, object filterValue = null)
|
||||
{
|
||||
var request = BuildRequest();
|
||||
request.AddParameter("page", pageNumber);
|
||||
@@ -112,8 +112,7 @@ namespace NzbDrone.Integration.Test.Client
|
||||
|
||||
if (filterKey != null && filterValue != null)
|
||||
{
|
||||
request.AddParameter("filterKey", filterKey);
|
||||
request.AddParameter("filterValue", filterValue);
|
||||
request.AddParameter(filterKey, filterValue);
|
||||
}
|
||||
|
||||
return Get<PagingResource<TResource>>(request);
|
||||
|
||||
@@ -25,9 +25,9 @@ namespace Radarr.Api.V3.Blocklist
|
||||
|
||||
[HttpGet]
|
||||
[Produces("application/json")]
|
||||
public PagingResource<BlocklistResource> GetBlocklist()
|
||||
public PagingResource<BlocklistResource> GetBlocklist([FromQuery] PagingRequestResource paging)
|
||||
{
|
||||
var pagingResource = Request.ReadPagingResourceFromRequest<BlocklistResource>();
|
||||
var pagingResource = new PagingResource<BlocklistResource>(paging);
|
||||
var pagingSpec = pagingResource.MapToPagingSpec<BlocklistResource, NzbDrone.Core.Blocklisting.Blocklist>("date", SortDirection.Descending);
|
||||
|
||||
return pagingSpec.ApplyToPage(_blocklistService.Paged, model => BlocklistResourceMapper.MapToResource(model, _formatCalculator));
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.Languages;
|
||||
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.Movies.Translations;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.SignalR;
|
||||
using Radarr.Http;
|
||||
@@ -27,28 +29,31 @@ namespace Radarr.Api.V3.Collections
|
||||
private readonly IMovieCollectionService _collectionService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IMovieMetadataService _movieMetadataService;
|
||||
private readonly IMovieTranslationService _movieTranslationService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IBuildFileNames _fileNameBuilder;
|
||||
private readonly INamingConfigService _namingService;
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public CollectionController(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
IMovieCollectionService collectionService,
|
||||
IMovieService movieService,
|
||||
IMovieMetadataService movieMetadataService,
|
||||
IMovieTranslationService movieTranslationService,
|
||||
IConfigService configService,
|
||||
IBuildFileNames fileNameBuilder,
|
||||
INamingConfigService namingService,
|
||||
IManageCommandQueue commandQueueManager,
|
||||
Logger logger)
|
||||
IManageCommandQueue commandQueueManager)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_collectionService = collectionService;
|
||||
_movieService = movieService;
|
||||
_movieMetadataService = movieMetadataService;
|
||||
_movieTranslationService = movieTranslationService;
|
||||
_configService = configService;
|
||||
_fileNameBuilder = fileNameBuilder;
|
||||
_namingService = namingService;
|
||||
_commandQueueManager = commandQueueManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
protected override CollectionResource GetResourceById(int id)
|
||||
@@ -75,8 +80,6 @@ namespace Radarr.Api.V3.Collections
|
||||
collectionResources = MapToResource(_collectionService.GetAllCollections()).ToList();
|
||||
}
|
||||
|
||||
_logger.Trace("Returning Collections");
|
||||
|
||||
return collectionResources;
|
||||
}
|
||||
|
||||
@@ -140,24 +143,38 @@ namespace Radarr.Api.V3.Collections
|
||||
{
|
||||
// Avoid calling for naming spec on every movie in filenamebuilder
|
||||
var namingConfig = _namingService.GetConfig();
|
||||
var collectionMovies = _movieMetadataService.GetMoviesWithCollections();
|
||||
var existingMoviesTmdbIds = _movieService.AllMovieWithCollectionsTmdbIds();
|
||||
var configLanguage = (Language)_configService.MovieInfoLanguage;
|
||||
|
||||
var allCollectionMovies = _movieMetadataService.GetMoviesWithCollections()
|
||||
.GroupBy(x => x.CollectionTmdbId)
|
||||
.ToDictionary(x => x.Key, x => (IEnumerable<MovieMetadata>)x);
|
||||
|
||||
var translations = _movieTranslationService.GetAllTranslationsForLanguage(configLanguage);
|
||||
var tdict = translations.ToDictionary(x => x.MovieMetadataId);
|
||||
|
||||
foreach (var collection in collections)
|
||||
{
|
||||
var resource = collection.ToResource();
|
||||
|
||||
foreach (var movie in collectionMovies.Where(m => m.CollectionTmdbId == collection.TmdbId))
|
||||
allCollectionMovies.TryGetValue(collection.TmdbId, out var collectionMovies);
|
||||
|
||||
if (collectionMovies != null)
|
||||
{
|
||||
var movieResource = movie.ToResource();
|
||||
movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { MovieMetadata = movie }, namingConfig);
|
||||
|
||||
if (!existingMoviesTmdbIds.Contains(movie.TmdbId))
|
||||
foreach (var movie in collectionMovies)
|
||||
{
|
||||
resource.MissingMovies++;
|
||||
}
|
||||
var translation = GetTranslationFromDict(tdict, movie, configLanguage);
|
||||
|
||||
resource.Movies.Add(movieResource);
|
||||
var movieResource = movie.ToResource(translation);
|
||||
movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { MovieMetadata = movie }, namingConfig);
|
||||
|
||||
if (!existingMoviesTmdbIds.Contains(movie.TmdbId))
|
||||
{
|
||||
resource.MissingMovies++;
|
||||
}
|
||||
|
||||
resource.Movies.Add(movieResource);
|
||||
}
|
||||
}
|
||||
|
||||
yield return resource;
|
||||
@@ -169,10 +186,14 @@ namespace Radarr.Api.V3.Collections
|
||||
var resource = collection.ToResource();
|
||||
var existingMoviesTmdbIds = _movieService.AllMovieWithCollectionsTmdbIds();
|
||||
var namingConfig = _namingService.GetConfig();
|
||||
var configLanguage = (Language)_configService.MovieInfoLanguage;
|
||||
|
||||
foreach (var movie in _movieMetadataService.GetMoviesByCollectionTmdbId(collection.TmdbId))
|
||||
{
|
||||
var movieResource = movie.ToResource();
|
||||
var translations = _movieTranslationService.GetAllTranslationsForMovieMetadata(movie.Id);
|
||||
var translation = GetMovieTranslation(translations, movie, configLanguage);
|
||||
|
||||
var movieResource = movie.ToResource(translation);
|
||||
movieResource.Folder = _fileNameBuilder.GetMovieFolder(new Movie { MovieMetadata = movie }, namingConfig);
|
||||
|
||||
if (!existingMoviesTmdbIds.Contains(movie.TmdbId))
|
||||
@@ -186,6 +207,54 @@ namespace Radarr.Api.V3.Collections
|
||||
return resource;
|
||||
}
|
||||
|
||||
private MovieTranslation GetMovieTranslation(List<MovieTranslation> translations, MovieMetadata movieMetadata, Language configLanguage)
|
||||
{
|
||||
if (configLanguage == Language.Original)
|
||||
{
|
||||
return new MovieTranslation
|
||||
{
|
||||
Title = movieMetadata.OriginalTitle,
|
||||
Overview = movieMetadata.Overview
|
||||
};
|
||||
}
|
||||
|
||||
var translation = translations.FirstOrDefault(t => t.Language == configLanguage && t.MovieMetadataId == movieMetadata.Id);
|
||||
|
||||
if (translation == null)
|
||||
{
|
||||
translation = new MovieTranslation
|
||||
{
|
||||
Title = movieMetadata.Title,
|
||||
Language = Language.English
|
||||
};
|
||||
}
|
||||
|
||||
return translation;
|
||||
}
|
||||
|
||||
private MovieTranslation GetTranslationFromDict(Dictionary<int, MovieTranslation> translations, MovieMetadata movieMetadata, Language configLanguage)
|
||||
{
|
||||
if (configLanguage == Language.Original)
|
||||
{
|
||||
return new MovieTranslation
|
||||
{
|
||||
Title = movieMetadata.OriginalTitle,
|
||||
Overview = movieMetadata.Overview
|
||||
};
|
||||
}
|
||||
|
||||
if (!translations.TryGetValue(movieMetadata.Id, out var translation))
|
||||
{
|
||||
translation = new MovieTranslation
|
||||
{
|
||||
Title = movieMetadata.Title,
|
||||
Language = Language.English
|
||||
};
|
||||
}
|
||||
|
||||
return translation;
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public void Handle(CollectionAddedEvent message)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Movies.Translations;
|
||||
|
||||
namespace Radarr.Api.V3.Collections
|
||||
{
|
||||
@@ -11,6 +12,7 @@ namespace Radarr.Api.V3.Collections
|
||||
public string Title { get; set; }
|
||||
public string CleanTitle { get; set; }
|
||||
public string SortTitle { get; set; }
|
||||
public MovieStatusType Status { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public int Runtime { get; set; }
|
||||
public List<MediaCover> Images { get; set; }
|
||||
@@ -22,18 +24,22 @@ namespace Radarr.Api.V3.Collections
|
||||
|
||||
public static class CollectionMovieResourceMapper
|
||||
{
|
||||
public static CollectionMovieResource ToResource(this MovieMetadata model)
|
||||
public static CollectionMovieResource ToResource(this MovieMetadata model, MovieTranslation movieTranslation = null)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var translatedTitle = movieTranslation?.Title ?? model.Title;
|
||||
var translatedOverview = movieTranslation?.Overview ?? model.Overview;
|
||||
|
||||
return new CollectionMovieResource
|
||||
{
|
||||
TmdbId = model.TmdbId,
|
||||
Title = model.Title,
|
||||
Overview = model.Overview,
|
||||
Title = translatedTitle,
|
||||
Status = model.Status,
|
||||
Overview = translatedOverview,
|
||||
SortTitle = model.SortTitle,
|
||||
Images = model.Images,
|
||||
ImdbId = model.ImdbId,
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Radarr.Api.V3.Config
|
||||
public int CheckForFinishedDownloadInterval { get; set; }
|
||||
|
||||
public bool AutoRedownloadFailed { get; set; }
|
||||
public bool AutoRedownloadFailedFromInteractiveSearch { get; set; }
|
||||
}
|
||||
|
||||
public static class DownloadClientConfigResourceMapper
|
||||
@@ -24,7 +25,8 @@ namespace Radarr.Api.V3.Config
|
||||
EnableCompletedDownloadHandling = model.EnableCompletedDownloadHandling,
|
||||
CheckForFinishedDownloadInterval = model.CheckForFinishedDownloadInterval,
|
||||
|
||||
AutoRedownloadFailed = model.AutoRedownloadFailed
|
||||
AutoRedownloadFailed = model.AutoRedownloadFailed,
|
||||
AutoRedownloadFailedFromInteractiveSearch = model.AutoRedownloadFailedFromInteractiveSearch
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
@@ -59,23 +60,20 @@ namespace Radarr.Api.V3.History
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public PagingResource<HistoryResource> GetHistory(bool includeMovie)
|
||||
[Produces("application/json")]
|
||||
public PagingResource<HistoryResource> GetHistory([FromQuery] PagingRequestResource paging, bool includeMovie, int? eventType, string downloadId)
|
||||
{
|
||||
var pagingResource = Request.ReadPagingResourceFromRequest<HistoryResource>();
|
||||
var pagingResource = new PagingResource<HistoryResource>(paging);
|
||||
var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, MovieHistory>("date", SortDirection.Descending);
|
||||
|
||||
var eventTypeFilter = pagingResource.Filters.FirstOrDefault(f => f.Key == "eventType");
|
||||
var downloadIdFilter = pagingResource.Filters.FirstOrDefault(f => f.Key == "downloadId");
|
||||
|
||||
if (eventTypeFilter != null)
|
||||
if (eventType.HasValue)
|
||||
{
|
||||
var filterValue = (MovieHistoryEventType)Convert.ToInt32(eventTypeFilter.Value);
|
||||
var filterValue = (MovieHistoryEventType)eventType.Value;
|
||||
pagingSpec.FilterExpressions.Add(v => v.EventType == filterValue);
|
||||
}
|
||||
|
||||
if (downloadIdFilter != null)
|
||||
if (downloadId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var downloadId = downloadIdFilter.Value;
|
||||
pagingSpec.FilterExpressions.Add(h => h.DownloadId == downloadId);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace Radarr.Api.V3.ImportLists
|
||||
private readonly IImportExclusionsService _importExclusionService;
|
||||
private readonly INamingConfigService _namingService;
|
||||
private readonly IMovieTranslationService _movieTranslationService;
|
||||
private readonly IMapCoversToLocal _coverMapper;
|
||||
private readonly IConfigService _configService;
|
||||
|
||||
public ImportListMoviesController(IMovieService movieService,
|
||||
@@ -40,7 +39,6 @@ namespace Radarr.Api.V3.ImportLists
|
||||
IImportExclusionsService importExclusionsService,
|
||||
INamingConfigService namingService,
|
||||
IMovieTranslationService movieTranslationService,
|
||||
IMapCoversToLocal coverMapper,
|
||||
IConfigService configService)
|
||||
{
|
||||
_movieService = movieService;
|
||||
@@ -52,7 +50,6 @@ namespace Radarr.Api.V3.ImportLists
|
||||
_importExclusionService = importExclusionsService;
|
||||
_namingService = namingService;
|
||||
_movieTranslationService = movieTranslationService;
|
||||
_coverMapper = coverMapper;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
@@ -118,7 +115,6 @@ namespace Radarr.Api.V3.ImportLists
|
||||
foreach (var currentMovie in movies)
|
||||
{
|
||||
var resource = currentMovie.ToResource();
|
||||
_coverMapper.ConvertToLocalUrls(0, resource.Images);
|
||||
|
||||
var poster = currentMovie.MovieMetadata.Value.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
|
||||
if (poster != null)
|
||||
@@ -148,7 +144,6 @@ namespace Radarr.Api.V3.ImportLists
|
||||
foreach (var currentMovie in movies)
|
||||
{
|
||||
var resource = currentMovie.ToResource();
|
||||
_coverMapper.ConvertToLocalUrls(0, resource.Images);
|
||||
|
||||
var poster = currentMovie.MovieMetadata.Value.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
|
||||
if (poster != null)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Instrumentation;
|
||||
using Radarr.Http;
|
||||
using Radarr.Http.Extensions;
|
||||
@@ -17,9 +17,10 @@ namespace Radarr.Api.V3.Logs
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public PagingResource<LogResource> GetLogs()
|
||||
[Produces("application/json")]
|
||||
public PagingResource<LogResource> GetLogs([FromQuery] PagingRequestResource paging, string level)
|
||||
{
|
||||
var pagingResource = Request.ReadPagingResourceFromRequest<LogResource>();
|
||||
var pagingResource = new PagingResource<LogResource>(paging);
|
||||
var pageSpec = pagingResource.MapToPagingSpec<LogResource, Log>();
|
||||
|
||||
if (pageSpec.SortKey == "time")
|
||||
@@ -27,11 +28,9 @@ namespace Radarr.Api.V3.Logs
|
||||
pageSpec.SortKey = "id";
|
||||
}
|
||||
|
||||
var levelFilter = pagingResource.Filters.FirstOrDefault(f => f.Key == "level");
|
||||
|
||||
if (levelFilter != null)
|
||||
if (level.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
switch (levelFilter.Value)
|
||||
switch (level)
|
||||
{
|
||||
case "fatal":
|
||||
pageSpec.FilterExpressions.Add(h => h.Level == "Fatal");
|
||||
|
||||
@@ -129,9 +129,10 @@ namespace Radarr.Api.V3.Queue
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public PagingResource<QueueResource> GetQueue(bool includeUnknownMovieItems = false, bool includeMovie = false)
|
||||
[Produces("application/json")]
|
||||
public PagingResource<QueueResource> GetQueue([FromQuery] PagingRequestResource paging, bool includeUnknownMovieItems = false, bool includeMovie = false)
|
||||
{
|
||||
var pagingResource = Request.ReadPagingResourceFromRequest<QueueResource>();
|
||||
var pagingResource = new PagingResource<QueueResource>(paging);
|
||||
var pagingSpec = pagingResource.MapToPagingSpec<QueueResource, NzbDrone.Core.Queue.Queue>("timeleft", SortDirection.Ascending);
|
||||
|
||||
return pagingSpec.ApplyToPage((spec) => GetQueue(spec, includeUnknownMovieItems), (q) => MapToResource(q, includeMovie));
|
||||
|
||||
@@ -162,25 +162,25 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Username": {
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"Password": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"RememberMe": {
|
||||
"rememberMe": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"encoding": {
|
||||
"Username": {
|
||||
"username": {
|
||||
"style": "form"
|
||||
},
|
||||
"Password": {
|
||||
"password": {
|
||||
"style": "form"
|
||||
},
|
||||
"RememberMe": {
|
||||
"rememberMe": {
|
||||
"style": "form"
|
||||
}
|
||||
}
|
||||
@@ -494,6 +494,40 @@
|
||||
"tags": [
|
||||
"Blocklist"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pageSize",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortKey",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortDirection",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SortDirection"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
@@ -2413,32 +2447,69 @@
|
||||
"History"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pageSize",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortKey",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortDirection",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SortDirection"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "includeMovie",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "eventType",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "downloadId",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HistoryResourcePagingResource"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HistoryResourcePagingResource"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HistoryResourcePagingResource"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4075,24 +4146,55 @@
|
||||
"tags": [
|
||||
"Log"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pageSize",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortKey",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortDirection",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SortDirection"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "level",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/LogResourcePagingResource"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/LogResourcePagingResource"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/LogResourcePagingResource"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5612,70 +5714,70 @@
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "RenameMovies",
|
||||
"name": "renameMovies",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ReplaceIllegalCharacters",
|
||||
"name": "replaceIllegalCharacters",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ColonReplacementFormat",
|
||||
"name": "colonReplacementFormat",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ColonReplacementFormat"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "StandardMovieFormat",
|
||||
"name": "standardMovieFormat",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "MovieFolderFormat",
|
||||
"name": "movieFolderFormat",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "IncludeQuality",
|
||||
"name": "includeQuality",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ReplaceSpaces",
|
||||
"name": "replaceSpaces",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Separator",
|
||||
"name": "separator",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "NumberStyle",
|
||||
"name": "numberStyle",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Id",
|
||||
"name": "id",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
@@ -5683,7 +5785,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ResourceName",
|
||||
"name": "resourceName",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
@@ -6597,6 +6699,38 @@
|
||||
"Queue"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pageSize",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortKey",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortDirection",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SortDirection"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "includeUnknownMovieItems",
|
||||
"in": "query",
|
||||
@@ -6618,20 +6752,10 @@
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/QueueResourcePagingResource"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/QueueResourcePagingResource"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/QueueResourcePagingResource"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8639,13 +8763,6 @@
|
||||
"sortDirection": {
|
||||
"$ref": "#/components/schemas/SortDirection"
|
||||
},
|
||||
"filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PagingResourceFilter"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"totalRecords": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
@@ -8691,6 +8808,9 @@
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/components/schemas/MovieStatusType"
|
||||
},
|
||||
"overview": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
@@ -9314,6 +9434,9 @@
|
||||
},
|
||||
"autoRedownloadFailed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"autoRedownloadFailedFromInteractiveSearch": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -9632,13 +9755,6 @@
|
||||
"sortDirection": {
|
||||
"$ref": "#/components/schemas/SortDirection"
|
||||
},
|
||||
"filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PagingResourceFilter"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"totalRecords": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
@@ -10363,13 +10479,6 @@
|
||||
"sortDirection": {
|
||||
"$ref": "#/components/schemas/SortDirection"
|
||||
},
|
||||
"filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PagingResourceFilter"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"totalRecords": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
@@ -11613,20 +11722,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PagingResourceFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ParseResource": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -12107,13 +12202,6 @@
|
||||
"sortDirection": {
|
||||
"$ref": "#/components/schemas/SortDirection"
|
||||
},
|
||||
"filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PagingResourceFilter"
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"totalRecords": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
|
||||
namespace Radarr.Http.Extensions
|
||||
{
|
||||
@@ -52,80 +51,6 @@ namespace Radarr.Http.Extensions
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static PagingResource<TResource> ReadPagingResourceFromRequest<TResource>(this HttpRequest request)
|
||||
{
|
||||
if (!int.TryParse(request.Query["PageSize"].ToString(), out var pageSize))
|
||||
{
|
||||
pageSize = 10;
|
||||
}
|
||||
|
||||
if (!int.TryParse(request.Query["Page"].ToString(), out var page))
|
||||
{
|
||||
page = 1;
|
||||
}
|
||||
|
||||
var pagingResource = new PagingResource<TResource>
|
||||
{
|
||||
PageSize = pageSize,
|
||||
Page = page,
|
||||
Filters = new List<PagingResourceFilter>()
|
||||
};
|
||||
|
||||
if (request.Query["SortKey"].Any())
|
||||
{
|
||||
var sortKey = request.Query["SortKey"].ToString();
|
||||
|
||||
if (!VALID_SORT_KEYS.Contains(sortKey) &&
|
||||
!TableMapping.Mapper.IsValidSortKey(sortKey))
|
||||
{
|
||||
throw new BadRequestException($"Invalid sort key {sortKey}");
|
||||
}
|
||||
|
||||
pagingResource.SortKey = sortKey;
|
||||
|
||||
if (request.Query["SortDirection"].Any())
|
||||
{
|
||||
pagingResource.SortDirection = request.Query["SortDirection"].ToString()
|
||||
.Equals("ascending", StringComparison.InvariantCultureIgnoreCase)
|
||||
? SortDirection.Ascending
|
||||
: SortDirection.Descending;
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility with v2
|
||||
if (request.Query["FilterKey"].Any())
|
||||
{
|
||||
var filter = new PagingResourceFilter
|
||||
{
|
||||
Key = request.Query["FilterKey"].ToString()
|
||||
};
|
||||
|
||||
if (request.Query["FilterValue"].Any())
|
||||
{
|
||||
filter.Value = request.Query["FilterValue"].ToString();
|
||||
}
|
||||
|
||||
pagingResource.Filters.Add(filter);
|
||||
}
|
||||
|
||||
// v3 uses filters in key=value format
|
||||
foreach (var pair in request.Query)
|
||||
{
|
||||
if (EXCLUDED_KEYS.Contains(pair.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pagingResource.Filters.Add(new PagingResourceFilter
|
||||
{
|
||||
Key = pair.Key,
|
||||
Value = pair.Value.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
return pagingResource;
|
||||
}
|
||||
|
||||
public static PagingResource<TResource> ApplyToPage<TResource, TModel>(this PagingSpec<TModel> pagingSpec, Func<PagingSpec<TModel>, PagingSpec<TModel>> function, Converter<TModel, TResource> mapper)
|
||||
{
|
||||
pagingSpec = function(pagingSpec);
|
||||
|
||||
@@ -1,17 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace Radarr.Http
|
||||
{
|
||||
public class PagingRequestResource
|
||||
{
|
||||
[DefaultValue(1)]
|
||||
public int? Page { get; set; }
|
||||
[DefaultValue(10)]
|
||||
public int? PageSize { get; set; }
|
||||
public string SortKey { get; set; }
|
||||
public SortDirection? SortDirection { get; set; }
|
||||
}
|
||||
|
||||
public class PagingResource<TResource>
|
||||
{
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public string SortKey { get; set; }
|
||||
public SortDirection SortDirection { get; set; }
|
||||
public List<PagingResourceFilter> Filters { get; set; }
|
||||
public int TotalRecords { get; set; }
|
||||
public List<TResource> Records { get; set; }
|
||||
|
||||
public PagingResource()
|
||||
{
|
||||
}
|
||||
|
||||
public PagingResource(PagingRequestResource requestResource)
|
||||
{
|
||||
Page = requestResource.Page ?? 1;
|
||||
PageSize = requestResource.PageSize ?? 10;
|
||||
SortKey = requestResource.SortKey;
|
||||
SortDirection = requestResource.SortDirection ?? SortDirection.Descending;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PagingResourceMapper
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Radarr.Http
|
||||
{
|
||||
public class PagingResourceFilter
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user