mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
Misc History Improvements
This commit is contained in:
@@ -2,255 +2,43 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||||
import DescriptionListItemDescription from 'Components/DescriptionList/DescriptionListItemDescription';
|
|
||||||
import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionListItemTitle';
|
|
||||||
import Link from 'Components/Link/Link';
|
|
||||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
|
||||||
import formatAge from 'Utilities/Number/formatAge';
|
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './HistoryDetails.css';
|
import styles from './HistoryDetails.css';
|
||||||
|
|
||||||
function HistoryDetails(props) {
|
function HistoryDetails(props) {
|
||||||
const {
|
const {
|
||||||
|
indexer,
|
||||||
eventType,
|
eventType,
|
||||||
sourceTitle,
|
data
|
||||||
data,
|
|
||||||
shortDateFormat,
|
|
||||||
timeFormat
|
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
if (eventType === 'grabbed') {
|
if (eventType === 'indexerQuery') {
|
||||||
const {
|
const {
|
||||||
indexer,
|
query,
|
||||||
releaseGroup,
|
queryResults
|
||||||
nzbInfoUrl,
|
|
||||||
downloadClient,
|
|
||||||
downloadId,
|
|
||||||
age,
|
|
||||||
ageHours,
|
|
||||||
ageMinutes,
|
|
||||||
publishedDate
|
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DescriptionList>
|
<DescriptionList>
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('Name')}
|
title={translate('Query')}
|
||||||
data={sourceTitle}
|
data={query}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
!!indexer &&
|
!!indexer &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Indexer')}
|
title={translate('Indexer')}
|
||||||
data={indexer}
|
data={indexer.name}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!!releaseGroup &&
|
!!data &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
title={'Query Results'}
|
||||||
title={translate('ReleaseGroup')}
|
data={queryResults}
|
||||||
data={releaseGroup}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!!nzbInfoUrl &&
|
|
||||||
<span>
|
|
||||||
<DescriptionListItemTitle>
|
|
||||||
Info URL
|
|
||||||
</DescriptionListItemTitle>
|
|
||||||
|
|
||||||
<DescriptionListItemDescription>
|
|
||||||
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
|
|
||||||
</DescriptionListItemDescription>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!!downloadClient &&
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('DownloadClient')}
|
|
||||||
data={downloadClient}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!!downloadId &&
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('GrabID')}
|
|
||||||
data={downloadId}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!!indexer &&
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('AgeWhenGrabbed')}
|
|
||||||
data={formatAge(age, ageHours, ageMinutes)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!!publishedDate &&
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('PublishedDate')}
|
|
||||||
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</DescriptionList>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType === 'downloadFailed') {
|
|
||||||
const {
|
|
||||||
message
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DescriptionList>
|
|
||||||
<DescriptionListItem
|
|
||||||
descriptionClassName={styles.description}
|
|
||||||
title={translate('Name')}
|
|
||||||
data={sourceTitle}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
|
||||||
!!message &&
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('Message')}
|
|
||||||
data={message}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</DescriptionList>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType === 'downloadFolderImported') {
|
|
||||||
const {
|
|
||||||
droppedPath,
|
|
||||||
importedPath
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DescriptionList>
|
|
||||||
<DescriptionListItem
|
|
||||||
descriptionClassName={styles.description}
|
|
||||||
title={translate('Name')}
|
|
||||||
data={sourceTitle}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
|
||||||
!!droppedPath &&
|
|
||||||
<DescriptionListItem
|
|
||||||
descriptionClassName={styles.description}
|
|
||||||
title={translate('Source')}
|
|
||||||
data={droppedPath}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!!importedPath &&
|
|
||||||
<DescriptionListItem
|
|
||||||
descriptionClassName={styles.description}
|
|
||||||
title={translate('ImportedTo')}
|
|
||||||
data={importedPath}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</DescriptionList>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType === 'movieFileDeleted') {
|
|
||||||
const {
|
|
||||||
reason
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
let reasonMessage = '';
|
|
||||||
|
|
||||||
switch (reason) {
|
|
||||||
case 'Manual':
|
|
||||||
reasonMessage = 'File was deleted by via UI';
|
|
||||||
break;
|
|
||||||
case 'MissingFromDisk':
|
|
||||||
reasonMessage = 'Prowlarr was unable to find the file on disk so it was removed';
|
|
||||||
break;
|
|
||||||
case 'Upgrade':
|
|
||||||
reasonMessage = 'File was deleted to import an upgrade';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
reasonMessage = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DescriptionList>
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('Name')}
|
|
||||||
data={sourceTitle}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('Reason')}
|
|
||||||
data={reasonMessage}
|
|
||||||
/>
|
|
||||||
</DescriptionList>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType === 'movieFileRenamed') {
|
|
||||||
const {
|
|
||||||
sourcePath,
|
|
||||||
sourceRelativePath,
|
|
||||||
path,
|
|
||||||
relativePath
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DescriptionList>
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('SourcePath')}
|
|
||||||
data={sourcePath}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('SourceRelativePath')}
|
|
||||||
data={sourceRelativePath}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('DestinationPath')}
|
|
||||||
data={path}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('DestinationRelativePath')}
|
|
||||||
data={relativePath}
|
|
||||||
/>
|
|
||||||
</DescriptionList>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType === 'downloadIgnored') {
|
|
||||||
const {
|
|
||||||
message
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DescriptionList>
|
|
||||||
<DescriptionListItem
|
|
||||||
descriptionClassName={styles.description}
|
|
||||||
title={translate('Name')}
|
|
||||||
data={sourceTitle}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
|
||||||
!!message &&
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('Message')}
|
|
||||||
data={message}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
@@ -262,15 +50,15 @@ function HistoryDetails(props) {
|
|||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('Name')}
|
title={translate('Name')}
|
||||||
data={sourceTitle}
|
data={data.query}
|
||||||
/>
|
/>
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryDetails.propTypes = {
|
HistoryDetails.propTypes = {
|
||||||
|
indexer: PropTypes.object.isRequired,
|
||||||
eventType: PropTypes.string.isRequired,
|
eventType: PropTypes.string.isRequired,
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
timeFormat: PropTypes.string.isRequired
|
timeFormat: PropTypes.string.isRequired
|
||||||
|
@@ -14,18 +14,8 @@ import styles from './HistoryDetailsModal.css';
|
|||||||
|
|
||||||
function getHeaderTitle(eventType) {
|
function getHeaderTitle(eventType) {
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'grabbed':
|
case 'indexerQuery':
|
||||||
return 'Grabbed';
|
return 'Indexer Query';
|
||||||
case 'downloadFailed':
|
|
||||||
return 'Download Failed';
|
|
||||||
case 'downloadFolderImported':
|
|
||||||
return 'Movie Imported';
|
|
||||||
case 'movieFileDeleted':
|
|
||||||
return 'Movie File Deleted';
|
|
||||||
case 'movieFileRenamed':
|
|
||||||
return 'Movie File Renamed';
|
|
||||||
case 'downloadIgnored':
|
|
||||||
return 'Download Ignored';
|
|
||||||
default:
|
default:
|
||||||
return 'Unknown';
|
return 'Unknown';
|
||||||
}
|
}
|
||||||
@@ -35,7 +25,7 @@ function HistoryDetailsModal(props) {
|
|||||||
const {
|
const {
|
||||||
isOpen,
|
isOpen,
|
||||||
eventType,
|
eventType,
|
||||||
sourceTitle,
|
indexer,
|
||||||
data,
|
data,
|
||||||
isMarkingAsFailed,
|
isMarkingAsFailed,
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
@@ -57,7 +47,7 @@ function HistoryDetailsModal(props) {
|
|||||||
<ModalBody>
|
<ModalBody>
|
||||||
<HistoryDetails
|
<HistoryDetails
|
||||||
eventType={eventType}
|
eventType={eventType}
|
||||||
sourceTitle={sourceTitle}
|
indexer={indexer}
|
||||||
data={data}
|
data={data}
|
||||||
shortDateFormat={shortDateFormat}
|
shortDateFormat={shortDateFormat}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
@@ -91,7 +81,7 @@ function HistoryDetailsModal(props) {
|
|||||||
HistoryDetailsModal.propTypes = {
|
HistoryDetailsModal.propTypes = {
|
||||||
isOpen: PropTypes.bool.isRequired,
|
isOpen: PropTypes.bool.isRequired,
|
||||||
eventType: PropTypes.string.isRequired,
|
eventType: PropTypes.string.isRequired,
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
indexer: PropTypes.object.isRequired,
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
isMarkingAsFailed: PropTypes.bool.isRequired,
|
isMarkingAsFailed: PropTypes.bool.isRequired,
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
|
@@ -7,20 +7,8 @@ import styles from './HistoryEventTypeCell.css';
|
|||||||
|
|
||||||
function getIconName(eventType) {
|
function getIconName(eventType) {
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'grabbed':
|
case 'indexerQuery':
|
||||||
return icons.DOWNLOADING;
|
return icons.SEARCH;
|
||||||
case 'movieFolderImported':
|
|
||||||
return icons.DRIVE;
|
|
||||||
case 'downloadFolderImported':
|
|
||||||
return icons.DOWNLOADED;
|
|
||||||
case 'downloadFailed':
|
|
||||||
return icons.DOWNLOADING;
|
|
||||||
case 'movieFileDeleted':
|
|
||||||
return icons.DELETE;
|
|
||||||
case 'movieFileRenamed':
|
|
||||||
return icons.ORGANIZE;
|
|
||||||
case 'downloadIgnored':
|
|
||||||
return icons.IGNORE;
|
|
||||||
default:
|
default:
|
||||||
return icons.UNKNOWN;
|
return icons.UNKNOWN;
|
||||||
}
|
}
|
||||||
@@ -35,31 +23,19 @@ function getIconKind(eventType) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTooltip(eventType, data) {
|
function getTooltip(eventType, data, indexer) {
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'grabbed':
|
case 'indexerQuery':
|
||||||
return `Movie grabbed from ${data.indexer} and sent to ${data.downloadClient}`;
|
return `Query "${data.query}" sent to ${indexer.name}`;
|
||||||
case 'movieFolderImported':
|
|
||||||
return 'Movie imported from movie folder';
|
|
||||||
case 'downloadFolderImported':
|
|
||||||
return 'Movie downloaded successfully and picked up from download client';
|
|
||||||
case 'downloadFailed':
|
|
||||||
return 'Movie download failed';
|
|
||||||
case 'movieFileDeleted':
|
|
||||||
return 'Movie file deleted';
|
|
||||||
case 'movieFileRenamed':
|
|
||||||
return 'Movie file renamed';
|
|
||||||
case 'downloadIgnored':
|
|
||||||
return 'Movie Download Ignored';
|
|
||||||
default:
|
default:
|
||||||
return 'Unknown event';
|
return 'Unknown event';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function HistoryEventTypeCell({ eventType, data }) {
|
function HistoryEventTypeCell({ eventType, data, indexer }) {
|
||||||
const iconName = getIconName(eventType);
|
const iconName = getIconName(eventType);
|
||||||
const iconKind = getIconKind(eventType);
|
const iconKind = getIconKind(eventType);
|
||||||
const tooltip = getTooltip(eventType, data);
|
const tooltip = getTooltip(eventType, data, indexer);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRowCell
|
<TableRowCell
|
||||||
@@ -76,7 +52,8 @@ function HistoryEventTypeCell({ eventType, data }) {
|
|||||||
|
|
||||||
HistoryEventTypeCell.propTypes = {
|
HistoryEventTypeCell.propTypes = {
|
||||||
eventType: PropTypes.string.isRequired,
|
eventType: PropTypes.string.isRequired,
|
||||||
data: PropTypes.object
|
data: PropTypes.object,
|
||||||
|
indexer: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
HistoryEventTypeCell.defaultProps = {
|
HistoryEventTypeCell.defaultProps = {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
.downloadClient {
|
.query {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 120px;
|
width: 120px;
|
||||||
|
@@ -48,9 +48,8 @@ class HistoryRow extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
movie,
|
indexer,
|
||||||
eventType,
|
eventType,
|
||||||
sourceTitle,
|
|
||||||
date,
|
date,
|
||||||
data,
|
data,
|
||||||
isMarkingAsFailed,
|
isMarkingAsFailed,
|
||||||
@@ -60,7 +59,7 @@ class HistoryRow extends Component {
|
|||||||
onMarkAsFailedPress
|
onMarkAsFailedPress
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!movie) {
|
if (!indexer) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +80,7 @@ class HistoryRow extends Component {
|
|||||||
return (
|
return (
|
||||||
<HistoryEventTypeCell
|
<HistoryEventTypeCell
|
||||||
key={name}
|
key={name}
|
||||||
|
indexer={indexer}
|
||||||
eventType={eventType}
|
eventType={eventType}
|
||||||
data={data}
|
data={data}
|
||||||
/>
|
/>
|
||||||
@@ -93,7 +93,40 @@ class HistoryRow extends Component {
|
|||||||
key={name}
|
key={name}
|
||||||
className={styles.indexer}
|
className={styles.indexer}
|
||||||
>
|
>
|
||||||
{movie.name}
|
{indexer.name}
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'successful') {
|
||||||
|
return (
|
||||||
|
<TableRowCell
|
||||||
|
key={name}
|
||||||
|
className={styles.indexer}
|
||||||
|
>
|
||||||
|
{data.successful}
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'elapsedTime') {
|
||||||
|
return (
|
||||||
|
<TableRowCell
|
||||||
|
key={name}
|
||||||
|
className={styles.indexer}
|
||||||
|
>
|
||||||
|
{`${data.elapsedTime}ms`}
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'query') {
|
||||||
|
return (
|
||||||
|
<TableRowCell
|
||||||
|
key={name}
|
||||||
|
className={styles.query}
|
||||||
|
>
|
||||||
|
{data.query}
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -128,8 +161,8 @@ class HistoryRow extends Component {
|
|||||||
<HistoryDetailsModal
|
<HistoryDetailsModal
|
||||||
isOpen={this.state.isDetailsModalOpen}
|
isOpen={this.state.isDetailsModalOpen}
|
||||||
eventType={eventType}
|
eventType={eventType}
|
||||||
sourceTitle={sourceTitle}
|
|
||||||
data={data}
|
data={data}
|
||||||
|
indexer={indexer}
|
||||||
isMarkingAsFailed={isMarkingAsFailed}
|
isMarkingAsFailed={isMarkingAsFailed}
|
||||||
shortDateFormat={shortDateFormat}
|
shortDateFormat={shortDateFormat}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
@@ -143,10 +176,9 @@ class HistoryRow extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HistoryRow.propTypes = {
|
HistoryRow.propTypes = {
|
||||||
movieId: PropTypes.number,
|
indexerId: PropTypes.number,
|
||||||
movie: PropTypes.object.isRequired,
|
indexer: PropTypes.object.isRequired,
|
||||||
eventType: PropTypes.string.isRequired,
|
eventType: PropTypes.string.isRequired,
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
|
||||||
date: PropTypes.string.isRequired,
|
date: PropTypes.string.isRequired,
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
isMarkingAsFailed: PropTypes.bool,
|
isMarkingAsFailed: PropTypes.bool,
|
||||||
|
@@ -11,9 +11,9 @@ function createMapStateToProps() {
|
|||||||
return createSelector(
|
return createSelector(
|
||||||
createIndexerSelector(),
|
createIndexerSelector(),
|
||||||
createUISettingsSelector(),
|
createUISettingsSelector(),
|
||||||
(movie, uiSettings) => {
|
(indexer, uiSettings) => {
|
||||||
return {
|
return {
|
||||||
movie,
|
indexer,
|
||||||
shortDateFormat: uiSettings.shortDateFormat,
|
shortDateFormat: uiSettings.shortDateFormat,
|
||||||
timeFormat: uiSettings.timeFormat
|
timeFormat: uiSettings.timeFormat
|
||||||
};
|
};
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import EditMovieModalContentConnector from './EditMovieModalContentConnector';
|
|
||||||
|
|
||||||
function EditMovieModal({ isOpen, onModalClose, ...otherProps }) {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<EditMovieModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditMovieModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditMovieModal;
|
|
@@ -1,39 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
|
||||||
import EditMovieModal from './EditMovieModal';
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
clearPendingChanges
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditMovieModalConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.props.clearPendingChanges({ section: 'movies' });
|
|
||||||
this.props.onModalClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<EditMovieModal
|
|
||||||
{...this.props}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditMovieModalConnector.propTypes = {
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
clearPendingChanges: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(undefined, mapDispatchToProps)(EditMovieModalConnector);
|
|
@@ -1,5 +0,0 @@
|
|||||||
.deleteButton {
|
|
||||||
composes: button from '~Components/Link/Button.css';
|
|
||||||
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
@@ -1,180 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Form from 'Components/Form/Form';
|
|
||||||
import FormGroup from 'Components/Form/FormGroup';
|
|
||||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
|
||||||
import FormLabel from 'Components/Form/FormLabel';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
|
||||||
import ModalContent from 'Components/Modal/ModalContent';
|
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
|
||||||
import { inputTypes, kinds } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './EditMovieModalContent.css';
|
|
||||||
|
|
||||||
class EditMovieModalContent extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isConfirmMoveModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onSavePress = () => {
|
|
||||||
const {
|
|
||||||
isPathChanging,
|
|
||||||
onSavePress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (isPathChanging && !this.state.isConfirmMoveModalOpen) {
|
|
||||||
this.setState({ isConfirmMoveModalOpen: true });
|
|
||||||
} else {
|
|
||||||
this.setState({ isConfirmMoveModalOpen: false });
|
|
||||||
|
|
||||||
onSavePress(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
title,
|
|
||||||
item,
|
|
||||||
isSaving,
|
|
||||||
originalPath,
|
|
||||||
onInputChange,
|
|
||||||
onModalClose,
|
|
||||||
onDeleteMoviePress,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
monitored,
|
|
||||||
qualityProfileId,
|
|
||||||
minimumAvailability,
|
|
||||||
// Id,
|
|
||||||
path,
|
|
||||||
tags
|
|
||||||
} = item;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
{translate('Edit')} - {title}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<Form
|
|
||||||
{...otherProps}
|
|
||||||
>
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Monitored')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="monitored"
|
|
||||||
helpText={translate('MonitoredHelpText')}
|
|
||||||
{...monitored}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('MinimumAvailability')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.AVAILABILITY_SELECT}
|
|
||||||
name="minimumAvailability"
|
|
||||||
{...minimumAvailability}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('QualityProfile')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
|
||||||
name="qualityProfileId"
|
|
||||||
{...qualityProfileId}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Path')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.PATH}
|
|
||||||
name="path"
|
|
||||||
{...path}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Tags')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.TAG}
|
|
||||||
name="tags"
|
|
||||||
{...tags}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Form>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button
|
|
||||||
className={styles.deleteButton}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
onPress={onDeleteMoviePress}
|
|
||||||
>
|
|
||||||
{translate('Delete')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
{translate('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<SpinnerButton
|
|
||||||
isSpinning={isSaving}
|
|
||||||
onPress={this.onSavePress}
|
|
||||||
>
|
|
||||||
{translate('Save')}
|
|
||||||
</SpinnerButton>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditMovieModalContent.propTypes = {
|
|
||||||
indexerId: PropTypes.number.isRequired,
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
isPathChanging: PropTypes.bool.isRequired,
|
|
||||||
originalPath: PropTypes.string.isRequired,
|
|
||||||
onInputChange: PropTypes.func.isRequired,
|
|
||||||
onSavePress: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
onDeleteMoviePress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditMovieModalContent;
|
|
@@ -1,115 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { saveMovie, setMovieValue } from 'Store/Actions/movieActions';
|
|
||||||
import createIndexerSelector from 'Store/Selectors/createIndexerSelector';
|
|
||||||
import selectSettings from 'Store/Selectors/selectSettings';
|
|
||||||
import EditMovieModalContent from './EditMovieModalContent';
|
|
||||||
|
|
||||||
function createIsPathChangingSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.movies.pendingChanges,
|
|
||||||
createIndexerSelector(),
|
|
||||||
(pendingChanges, movie) => {
|
|
||||||
const path = pendingChanges.path;
|
|
||||||
|
|
||||||
if (path == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return movie.path !== path;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.movies,
|
|
||||||
createIndexerSelector(),
|
|
||||||
createIsPathChangingSelector(),
|
|
||||||
(moviesState, movie, isPathChanging) => {
|
|
||||||
const {
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
pendingChanges
|
|
||||||
} = moviesState;
|
|
||||||
|
|
||||||
const movieSettings = {
|
|
||||||
monitored: movie.monitored,
|
|
||||||
qualityProfileId: movie.qualityProfileId,
|
|
||||||
minimumAvailability: movie.minimumAvailability,
|
|
||||||
path: movie.path,
|
|
||||||
tags: movie.tags
|
|
||||||
};
|
|
||||||
|
|
||||||
const settings = selectSettings(movieSettings, pendingChanges, saveError);
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: movie.title,
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
isPathChanging,
|
|
||||||
originalPath: movie.path,
|
|
||||||
item: settings.settings,
|
|
||||||
...settings
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
dispatchSetMovieValue: setMovieValue,
|
|
||||||
dispatchSaveMovie: saveMovie
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditMovieModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
|
|
||||||
this.props.onModalClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onInputChange = ({ name, value }) => {
|
|
||||||
this.props.dispatchSetMovieValue({ name, value });
|
|
||||||
}
|
|
||||||
|
|
||||||
onSavePress = (moveFiles) => {
|
|
||||||
this.props.dispatchSaveMovie({
|
|
||||||
id: this.props.indexerId,
|
|
||||||
moveFiles
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<EditMovieModalContent
|
|
||||||
{...this.props}
|
|
||||||
onInputChange={this.onInputChange}
|
|
||||||
onSavePress={this.onSavePress}
|
|
||||||
onMoveMoviePress={this.onMoveMoviePress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditMovieModalContentConnector.propTypes = {
|
|
||||||
indexerId: PropTypes.number,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
dispatchSetMovieValue: PropTypes.func.isRequired,
|
|
||||||
dispatchSaveMovie: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(EditMovieModalContentConnector);
|
|
@@ -47,6 +47,15 @@ function MovieIndexSortMenu(props) {
|
|||||||
{translate('Added')}
|
{translate('Added')}
|
||||||
</SortMenuItem>
|
</SortMenuItem>
|
||||||
|
|
||||||
|
<SortMenuItem
|
||||||
|
name="priority"
|
||||||
|
sortKey={sortKey}
|
||||||
|
sortDirection={sortDirection}
|
||||||
|
onPress={onSortSelect}
|
||||||
|
>
|
||||||
|
{'Priority'}
|
||||||
|
</SortMenuItem>
|
||||||
|
|
||||||
<SortMenuItem
|
<SortMenuItem
|
||||||
name="protocol"
|
name="protocol"
|
||||||
sortKey={sortKey}
|
sortKey={sortKey}
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
.progress {
|
|
||||||
composes: container from '~Components/ProgressBar.css';
|
|
||||||
|
|
||||||
border-radius: 0;
|
|
||||||
background-color: #5b5b5b;
|
|
||||||
color: $white;
|
|
||||||
transition: width 200ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progressBar {
|
|
||||||
composes: progressBar from '~Components/ProgressBar.css';
|
|
||||||
|
|
||||||
transition: width 200ms ease;
|
|
||||||
}
|
|
@@ -1,65 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import ProgressBar from 'Components/ProgressBar';
|
|
||||||
import { sizes } from 'Helpers/Props';
|
|
||||||
import getProgressBarKind from 'Utilities/Movie/getProgressBarKind';
|
|
||||||
import getQueueStatusText from 'Utilities/Movie/getQueueStatusText';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './MovieIndexProgressBar.css';
|
|
||||||
|
|
||||||
function MovieIndexProgressBar(props) {
|
|
||||||
const {
|
|
||||||
monitored,
|
|
||||||
status,
|
|
||||||
hasFile,
|
|
||||||
isAvailable,
|
|
||||||
posterWidth,
|
|
||||||
detailedProgressBar,
|
|
||||||
queueStatus,
|
|
||||||
queueState
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const progress = 100;
|
|
||||||
const queueStatusText = getQueueStatusText(queueStatus, queueState);
|
|
||||||
let movieStatus = (status === 'released' && hasFile) ? 'downloaded' : status;
|
|
||||||
|
|
||||||
if (movieStatus === 'deleted') {
|
|
||||||
movieStatus = 'Missing';
|
|
||||||
|
|
||||||
if (hasFile) {
|
|
||||||
movieStatus = 'Downloaded';
|
|
||||||
}
|
|
||||||
} else if (hasFile) {
|
|
||||||
movieStatus = 'Downloaded';
|
|
||||||
} else if (isAvailable && !hasFile) {
|
|
||||||
movieStatus = 'Missing';
|
|
||||||
} else {
|
|
||||||
movieStatus = 'NotAvailable';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ProgressBar
|
|
||||||
className={styles.progressBar}
|
|
||||||
containerClassName={styles.progress}
|
|
||||||
progress={progress}
|
|
||||||
kind={getProgressBarKind(status, monitored, hasFile, isAvailable, queueStatusText)}
|
|
||||||
size={detailedProgressBar ? sizes.MEDIUM : sizes.SMALL}
|
|
||||||
showText={detailedProgressBar}
|
|
||||||
width={posterWidth}
|
|
||||||
text={(queueStatusText) ? queueStatusText : translate(movieStatus)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieIndexProgressBar.propTypes = {
|
|
||||||
monitored: PropTypes.bool.isRequired,
|
|
||||||
hasFile: PropTypes.bool.isRequired,
|
|
||||||
isAvailable: PropTypes.bool.isRequired,
|
|
||||||
status: PropTypes.string.isRequired,
|
|
||||||
posterWidth: PropTypes.number.isRequired,
|
|
||||||
detailedProgressBar: PropTypes.bool.isRequired,
|
|
||||||
queueStatus: PropTypes.string,
|
|
||||||
queueState: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieIndexProgressBar;
|
|
9
frontend/src/Indexer/Index/Table/IndexerStatusCell.css
Normal file
9
frontend/src/Indexer/Index/Table/IndexerStatusCell.css
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.status {
|
||||||
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusIcon {
|
||||||
|
width: 20px !important;
|
||||||
|
}
|
44
frontend/src/Indexer/Index/Table/IndexerStatusCell.js
Normal file
44
frontend/src/Indexer/Index/Table/IndexerStatusCell.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import VirtualTableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import styles from './IndexerStatusCell.css';
|
||||||
|
|
||||||
|
function IndexerStatusCell(props) {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
enabled,
|
||||||
|
component: Component,
|
||||||
|
...otherProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Component
|
||||||
|
className={className}
|
||||||
|
{...otherProps}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
!enabled &&
|
||||||
|
<Icon
|
||||||
|
className={styles.statusIcon}
|
||||||
|
name={icons.BLACKLIST}
|
||||||
|
title={enabled ? 'Indexer is Enabled' : 'Indexer is Disabled'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</Component>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexerStatusCell.propTypes = {
|
||||||
|
className: PropTypes.string.isRequired,
|
||||||
|
enabled: PropTypes.bool.isRequired,
|
||||||
|
component: PropTypes.elementType
|
||||||
|
};
|
||||||
|
|
||||||
|
IndexerStatusCell.defaultProps = {
|
||||||
|
className: styles.status,
|
||||||
|
component: VirtualTableRowCell
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IndexerStatusCell;
|
@@ -10,6 +10,7 @@
|
|||||||
flex: 4 0 110px;
|
flex: 4 0 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.priority,
|
||||||
.privacy,
|
.privacy,
|
||||||
.protocol {
|
.protocol {
|
||||||
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
|
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
flex: 4 0 110px;
|
flex: 4 0 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.priority,
|
||||||
.protocol,
|
.protocol,
|
||||||
.privacy {
|
.privacy {
|
||||||
composes: cell;
|
composes: cell;
|
||||||
|
@@ -5,11 +5,14 @@ import IconButton from 'Components/Link/IconButton';
|
|||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||||
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
||||||
import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
|
import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import TagListConnector from 'Components/TagListConnector';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
import DeleteIndexerModal from 'Indexer/Delete/DeleteIndexerModal';
|
import DeleteIndexerModal from 'Indexer/Delete/DeleteIndexerModal';
|
||||||
import EditIndexerModalConnector from 'Settings/Indexers/Indexers/EditIndexerModalConnector';
|
import EditIndexerModalConnector from 'Settings/Indexers/Indexers/EditIndexerModalConnector';
|
||||||
|
import titleCase from 'Utilities/String/titleCase';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import CapabilitiesLabel from './CapabilitiesLabel';
|
import CapabilitiesLabel from './CapabilitiesLabel';
|
||||||
|
import IndexerStatusCell from './IndexerStatusCell';
|
||||||
import ProtocolLabel from './ProtocolLabel';
|
import ProtocolLabel from './ProtocolLabel';
|
||||||
import styles from './MovieIndexRow.css';
|
import styles from './MovieIndexRow.css';
|
||||||
|
|
||||||
@@ -61,8 +64,10 @@ class MovieIndexRow extends Component {
|
|||||||
enableRss,
|
enableRss,
|
||||||
enableAutomaticSearch,
|
enableAutomaticSearch,
|
||||||
enableInteractiveSearch,
|
enableInteractiveSearch,
|
||||||
|
tags,
|
||||||
protocol,
|
protocol,
|
||||||
privacy,
|
privacy,
|
||||||
|
priority,
|
||||||
added,
|
added,
|
||||||
capabilities,
|
capabilities,
|
||||||
columns,
|
columns,
|
||||||
@@ -103,28 +108,13 @@ class MovieIndexRow extends Component {
|
|||||||
|
|
||||||
if (column.name === 'status') {
|
if (column.name === 'status') {
|
||||||
return (
|
return (
|
||||||
<VirtualTableRowCell
|
<IndexerStatusCell
|
||||||
key={column.name}
|
key={column.name}
|
||||||
className={styles[column.name]}
|
className={styles[column.name]}
|
||||||
>
|
enabled={enableRss || enableAutomaticSearch || enableInteractiveSearch}
|
||||||
{
|
status={status}
|
||||||
enableRss || enableAutomaticSearch || enableInteractiveSearch ?
|
component={VirtualTableRowCell}
|
||||||
<Label kind={kinds.SUCCESS}>
|
/>
|
||||||
{'Enabled'}
|
|
||||||
</Label>:
|
|
||||||
null
|
|
||||||
}
|
|
||||||
{
|
|
||||||
!enableRss && !enableAutomaticSearch && !enableInteractiveSearch ?
|
|
||||||
<Label
|
|
||||||
kind={kinds.DISABLED}
|
|
||||||
outline={true}
|
|
||||||
>
|
|
||||||
{translate('Disabled')}
|
|
||||||
</Label> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</VirtualTableRowCell>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,12 +136,23 @@ class MovieIndexRow extends Component {
|
|||||||
className={styles[column.name]}
|
className={styles[column.name]}
|
||||||
>
|
>
|
||||||
<Label>
|
<Label>
|
||||||
{privacy}
|
{titleCase(privacy)}
|
||||||
</Label>
|
</Label>
|
||||||
</VirtualTableRowCell>
|
</VirtualTableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (column.name === 'priority') {
|
||||||
|
return (
|
||||||
|
<VirtualTableRowCell
|
||||||
|
key={column.name}
|
||||||
|
className={styles[column.name]}
|
||||||
|
>
|
||||||
|
{priority}
|
||||||
|
</VirtualTableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (column.name === 'protocol') {
|
if (column.name === 'protocol') {
|
||||||
return (
|
return (
|
||||||
<VirtualTableRowCell
|
<VirtualTableRowCell
|
||||||
@@ -189,6 +190,19 @@ class MovieIndexRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (column.name === 'tags') {
|
||||||
|
return (
|
||||||
|
<VirtualTableRowCell
|
||||||
|
key={column.name}
|
||||||
|
className={styles[column.name]}
|
||||||
|
>
|
||||||
|
<TagListConnector
|
||||||
|
tags={tags}
|
||||||
|
/>
|
||||||
|
</VirtualTableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (column.name === 'actions') {
|
if (column.name === 'actions') {
|
||||||
return (
|
return (
|
||||||
<VirtualTableRowCell
|
<VirtualTableRowCell
|
||||||
@@ -234,6 +248,7 @@ MovieIndexRow.propTypes = {
|
|||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
protocol: PropTypes.string.isRequired,
|
protocol: PropTypes.string.isRequired,
|
||||||
privacy: PropTypes.string.isRequired,
|
privacy: PropTypes.string.isRequired,
|
||||||
|
priority: PropTypes.number.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
enableRss: PropTypes.bool.isRequired,
|
enableRss: PropTypes.bool.isRequired,
|
||||||
enableAutomaticSearch: PropTypes.bool.isRequired,
|
enableAutomaticSearch: PropTypes.bool.isRequired,
|
||||||
@@ -248,4 +263,8 @@ MovieIndexRow.propTypes = {
|
|||||||
onSelectedChange: PropTypes.func.isRequired
|
onSelectedChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MovieIndexRow.defaultProps = {
|
||||||
|
tags: []
|
||||||
|
};
|
||||||
|
|
||||||
export default MovieIndexRow;
|
export default MovieIndexRow;
|
||||||
|
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||||
import { align } from 'Helpers/Props';
|
import { align } from 'Helpers/Props';
|
||||||
import MovieIndexFilterModalConnector from 'Indexer/Index/MovieIndexFilterModalConnector';
|
import SearchIndexFilterModalConnector from 'Search/SearchIndexFilterModalConnector';
|
||||||
|
|
||||||
function SearchIndexFilterMenu(props) {
|
function SearchIndexFilterMenu(props) {
|
||||||
const {
|
const {
|
||||||
@@ -20,7 +20,7 @@ function SearchIndexFilterMenu(props) {
|
|||||||
selectedFilterKey={selectedFilterKey}
|
selectedFilterKey={selectedFilterKey}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
customFilters={customFilters}
|
customFilters={customFilters}
|
||||||
filterModalConnectorComponent={MovieIndexFilterModalConnector}
|
filterModalConnectorComponent={SearchIndexFilterModalConnector}
|
||||||
onFilterSelect={onFilterSelect}
|
onFilterSelect={onFilterSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
24
frontend/src/Search/SearchIndexFilterModalConnector.js
Normal file
24
frontend/src/Search/SearchIndexFilterModalConnector.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import FilterModal from 'Components/Filter/FilterModal';
|
||||||
|
import { setReleasesFilter } from 'Store/Actions/releaseActions';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state) => state.releases.items,
|
||||||
|
(state) => state.releases.filterBuilderProps,
|
||||||
|
(sectionItems, filterBuilderProps) => {
|
||||||
|
return {
|
||||||
|
sectionItems,
|
||||||
|
filterBuilderProps,
|
||||||
|
customFilterType: 'releases'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
dispatchSetFilter: setReleasesFilter
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, mapDispatchToProps)(FilterModal);
|
@@ -15,17 +15,7 @@ function NotificationEventItems(props) {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
onGrab,
|
|
||||||
onDownload,
|
|
||||||
onUpgrade,
|
|
||||||
onRename,
|
|
||||||
onDelete,
|
|
||||||
onHealthIssue,
|
onHealthIssue,
|
||||||
supportsOnGrab,
|
|
||||||
supportsOnDownload,
|
|
||||||
supportsOnUpgrade,
|
|
||||||
supportsOnRename,
|
|
||||||
supportsOnDelete,
|
|
||||||
supportsOnHealthIssue,
|
supportsOnHealthIssue,
|
||||||
includeHealthWarnings
|
includeHealthWarnings
|
||||||
} = item;
|
} = item;
|
||||||
@@ -39,64 +29,6 @@ function NotificationEventItems(props) {
|
|||||||
link="https://github.com/Prowlarr/Prowlarr/wiki/Connections"
|
link="https://github.com/Prowlarr/Prowlarr/wiki/Connections"
|
||||||
/>
|
/>
|
||||||
<div className={styles.events}>
|
<div className={styles.events}>
|
||||||
<div>
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="onGrab"
|
|
||||||
helpText={translate('OnGrabHelpText')}
|
|
||||||
isDisabled={!supportsOnGrab.value}
|
|
||||||
{...onGrab}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="onDownload"
|
|
||||||
helpText={translate('OnDownloadHelpText')}
|
|
||||||
isDisabled={!supportsOnDownload.value}
|
|
||||||
{...onDownload}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
onDownload.value &&
|
|
||||||
<div>
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="onUpgrade"
|
|
||||||
helpText={translate('OnUpgradeHelpText')}
|
|
||||||
isDisabled={!supportsOnUpgrade.value}
|
|
||||||
{...onUpgrade}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="onRename"
|
|
||||||
helpText={translate('OnRenameHelpText')}
|
|
||||||
isDisabled={!supportsOnRename.value}
|
|
||||||
{...onRename}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="onDelete"
|
|
||||||
helpText={translate('OnDeleteHelpText')}
|
|
||||||
isDisabled={!supportsOnDelete.value}
|
|
||||||
{...onDelete}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
|
@@ -37,7 +37,13 @@ export const defaultState = {
|
|||||||
{
|
{
|
||||||
name: 'indexer',
|
name: 'indexer',
|
||||||
label: 'Indexer',
|
label: 'Indexer',
|
||||||
isSortable: true,
|
isSortable: false,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'query',
|
||||||
|
label: 'Query',
|
||||||
|
isSortable: false,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -46,6 +52,18 @@ export const defaultState = {
|
|||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'successful',
|
||||||
|
label: 'Successful',
|
||||||
|
isSortable: false,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'elapsedTime',
|
||||||
|
label: 'Elapsed Time',
|
||||||
|
isSortable: false,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'details',
|
name: 'details',
|
||||||
columnLabel: translate('Details'),
|
columnLabel: translate('Details'),
|
||||||
|
@@ -68,6 +68,12 @@ export const defaultState = {
|
|||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'priority',
|
||||||
|
label: translate('Priority'),
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'added',
|
name: 'added',
|
||||||
label: translate('Added'),
|
label: translate('Added'),
|
||||||
@@ -115,6 +121,17 @@ export const defaultState = {
|
|||||||
type: filterBuilderTypes.DATE,
|
type: filterBuilderTypes.DATE,
|
||||||
valueType: filterBuilderValueTypes.DATE
|
valueType: filterBuilderValueTypes.DATE
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'priority',
|
||||||
|
label: 'Priority',
|
||||||
|
type: filterBuilderTypes.NUMBER
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'protocol',
|
||||||
|
label: 'Protocol',
|
||||||
|
type: filterBuilderTypes.EXACT,
|
||||||
|
valueType: filterBuilderValueTypes.PROTOCOL
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'tags',
|
name: 'tags',
|
||||||
label: translate('Tags'),
|
label: translate('Tags'),
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createAction } from 'redux-actions';
|
import { createAction } from 'redux-actions';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import { filterBuilderTypes, filterBuilderValueTypes, filterTypes, icons, sortDirections } from 'Helpers/Props';
|
import { filterBuilderTypes, filterBuilderValueTypes, icons, sortDirections } from 'Helpers/Props';
|
||||||
import { createThunk, handleThunks } from 'Store/thunks';
|
import { createThunk, handleThunks } from 'Store/thunks';
|
||||||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
@@ -100,17 +100,6 @@ export const defaultState = {
|
|||||||
return releaseWeight + 1000000;
|
return releaseWeight + 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
return releaseWeight;
|
|
||||||
},
|
|
||||||
|
|
||||||
rejections: function(item, direction) {
|
|
||||||
const rejections = item.rejections;
|
|
||||||
const releaseWeight = item.releaseWeight;
|
|
||||||
|
|
||||||
if (rejections.length !== 0) {
|
|
||||||
return releaseWeight + 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
return releaseWeight;
|
return releaseWeight;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -123,50 +112,6 @@ export const defaultState = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
filterPredicates: {
|
|
||||||
quality: function(item, value, type) {
|
|
||||||
const qualityId = item.quality.quality.id;
|
|
||||||
|
|
||||||
if (type === filterTypes.EQUAL) {
|
|
||||||
return qualityId === value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === filterTypes.NOT_EQUAL) {
|
|
||||||
return qualityId !== value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to false
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
rejectionCount: function(item, value, type) {
|
|
||||||
const rejectionCount = item.rejections.length;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case filterTypes.EQUAL:
|
|
||||||
return rejectionCount === value;
|
|
||||||
|
|
||||||
case filterTypes.GREATER_THAN:
|
|
||||||
return rejectionCount > value;
|
|
||||||
|
|
||||||
case filterTypes.GREATER_THAN_OR_EQUAL:
|
|
||||||
return rejectionCount >= value;
|
|
||||||
|
|
||||||
case filterTypes.LESS_THAN:
|
|
||||||
return rejectionCount < value;
|
|
||||||
|
|
||||||
case filterTypes.LESS_THAN_OR_EQUAL:
|
|
||||||
return rejectionCount <= value;
|
|
||||||
|
|
||||||
case filterTypes.NOT_EQUAL:
|
|
||||||
return rejectionCount !== value;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
filterBuilderProps: [
|
filterBuilderProps: [
|
||||||
{
|
{
|
||||||
name: 'title',
|
name: 'title',
|
||||||
@@ -204,17 +149,6 @@ export const defaultState = {
|
|||||||
name: 'peers',
|
name: 'peers',
|
||||||
label: translate('Peers'),
|
label: translate('Peers'),
|
||||||
type: filterBuilderTypes.NUMBER
|
type: filterBuilderTypes.NUMBER
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'quality',
|
|
||||||
label: translate('Quality'),
|
|
||||||
type: filterBuilderTypes.EXACT,
|
|
||||||
valueType: filterBuilderValueTypes.QUALITY
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'rejectionCount',
|
|
||||||
label: translate('RejectionCount'),
|
|
||||||
type: filterBuilderTypes.NUMBER
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
selectedFilterKey: 'all'
|
selectedFilterKey: 'all'
|
||||||
@@ -235,7 +169,7 @@ export const SET_RELEASES_SORT = 'releases/setReleasesSort';
|
|||||||
export const CLEAR_RELEASES = 'releases/clearReleases';
|
export const CLEAR_RELEASES = 'releases/clearReleases';
|
||||||
export const GRAB_RELEASE = 'releases/grabRelease';
|
export const GRAB_RELEASE = 'releases/grabRelease';
|
||||||
export const UPDATE_RELEASE = 'releases/updateRelease';
|
export const UPDATE_RELEASE = 'releases/updateRelease';
|
||||||
export const SET_RELEASES_FILTER = 'releases/setMovieReleasesFilter';
|
export const SET_RELEASES_FILTER = 'releases/setReleasesFilter';
|
||||||
export const SET_RELEASES_TABLE_OPTION = 'releases/setReleasesTableOption';
|
export const SET_RELEASES_TABLE_OPTION = 'releases/setReleasesTableOption';
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using NzbDrone.Core.Indexers;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using NzbDrone.Core.ThingiProvider.Events;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Applications
|
namespace NzbDrone.Core.Applications
|
||||||
{
|
{
|
||||||
@@ -10,7 +14,7 @@ namespace NzbDrone.Core.Applications
|
|||||||
void DeleteAllForApp(int appId);
|
void DeleteAllForApp(int appId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AppIndexerMapService : IAppIndexerMapService
|
public class AppIndexerMapService : IAppIndexerMapService, IHandle<ProviderDeletedEvent<IApplication>>
|
||||||
{
|
{
|
||||||
private readonly IAppIndexerMapRepository _appIndexerMapRepository;
|
private readonly IAppIndexerMapRepository _appIndexerMapRepository;
|
||||||
|
|
||||||
@@ -33,5 +37,10 @@ namespace NzbDrone.Core.Applications
|
|||||||
{
|
{
|
||||||
return _appIndexerMapRepository.Insert(appIndexerMap);
|
return _appIndexerMapRepository.Insert(appIndexerMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Handle(ProviderDeletedEvent<IApplication> message)
|
||||||
|
{
|
||||||
|
_appIndexerMapRepository.DeleteAllForApp(message.ProviderId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,6 @@ namespace NzbDrone.Core.Datastore.Migration
|
|||||||
|
|
||||||
Create.TableForModel("History")
|
Create.TableForModel("History")
|
||||||
.WithColumn("IndexerId").AsInt32()
|
.WithColumn("IndexerId").AsInt32()
|
||||||
.WithColumn("SourceTitle").AsString()
|
|
||||||
.WithColumn("Date").AsDateTime()
|
.WithColumn("Date").AsDateTime()
|
||||||
.WithColumn("Data").AsString()
|
.WithColumn("Data").AsString()
|
||||||
.WithColumn("EventType").AsInt32().Nullable()
|
.WithColumn("EventType").AsInt32().Nullable()
|
||||||
|
@@ -12,7 +12,6 @@ namespace NzbDrone.Core.History
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int IndexerId { get; set; }
|
public int IndexerId { get; set; }
|
||||||
public string SourceTitle { get; set; }
|
|
||||||
public DateTime Date { get; set; }
|
public DateTime Date { get; set; }
|
||||||
public HistoryEventType EventType { get; set; }
|
public HistoryEventType EventType { get; set; }
|
||||||
public Dictionary<string, string> Data { get; set; }
|
public Dictionary<string, string> Data { get; set; }
|
||||||
|
@@ -86,10 +86,14 @@ namespace NzbDrone.Core.History
|
|||||||
{
|
{
|
||||||
Date = DateTime.UtcNow,
|
Date = DateTime.UtcNow,
|
||||||
IndexerId = message.IndexerId,
|
IndexerId = message.IndexerId,
|
||||||
EventType = HistoryEventType.IndexerQuery,
|
EventType = HistoryEventType.IndexerQuery
|
||||||
SourceTitle = message.Query
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
history.Data.Add("ElapsedTime", message.Time.ToString());
|
||||||
|
history.Data.Add("Query", message.Query.SceneTitles.FirstOrDefault() ?? string.Empty);
|
||||||
|
history.Data.Add("Successful", message.Successful.ToString());
|
||||||
|
history.Data.Add("QueryResults", message.Results.HasValue ? message.Results.ToString() : null);
|
||||||
|
|
||||||
_historyRepository.Insert(history);
|
_historyRepository.Insert(history);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NLog;
|
using NLog;
|
||||||
@@ -53,10 +54,15 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
{
|
{
|
||||||
var spec = new TSpec()
|
var spec = new TSpec()
|
||||||
{
|
{
|
||||||
InteractiveSearch = interactiveSearch
|
InteractiveSearch = interactiveSearch,
|
||||||
|
SceneTitles = new List<string>()
|
||||||
};
|
};
|
||||||
|
|
||||||
spec.SceneTitles = new List<string> { query };
|
if (query.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
spec.SceneTitles.Add(query);
|
||||||
|
}
|
||||||
|
|
||||||
spec.IndexerIds = indexerIds;
|
spec.IndexerIds = indexerIds;
|
||||||
|
|
||||||
return spec;
|
return spec;
|
||||||
@@ -86,21 +92,25 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
|
|
||||||
taskList.Add(taskFactory.StartNew(() =>
|
taskList.Add(taskFactory.StartNew(() =>
|
||||||
{
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var indexerReports = searchAction(indexerLocal);
|
var indexerReports = searchAction(indexerLocal);
|
||||||
|
|
||||||
_eventAggregator.PublishEvent(new IndexerQueryEvent(indexer.Definition.Id, criteriaBase.QueryTitles.Join(", ")));
|
|
||||||
|
|
||||||
lock (reports)
|
lock (reports)
|
||||||
{
|
{
|
||||||
reports.AddRange(indexerReports);
|
reports.AddRange(indexerReports);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_eventAggregator.PublishEvent(new IndexerQueryEvent(indexer.Definition.Id, criteriaBase, sw.ElapsedMilliseconds, true, indexerReports.Count()));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
_eventAggregator.PublishEvent(new IndexerQueryEvent(indexer.Definition.Id, criteriaBase, sw.ElapsedMilliseconds, false));
|
||||||
_logger.Error(e, "Error while searching for {0}", criteriaBase);
|
_logger.Error(e, "Error while searching for {0}", criteriaBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
}).LogExceptions());
|
}).LogExceptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,16 +1,23 @@
|
|||||||
using NzbDrone.Common.Messaging;
|
using NzbDrone.Common.Messaging;
|
||||||
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Indexers
|
namespace NzbDrone.Core.Indexers
|
||||||
{
|
{
|
||||||
public class IndexerQueryEvent : IEvent
|
public class IndexerQueryEvent : IEvent
|
||||||
{
|
{
|
||||||
public int IndexerId { get; set; }
|
public int IndexerId { get; set; }
|
||||||
public string Query { get; set; }
|
public SearchCriteriaBase Query { get; set; }
|
||||||
|
public long Time { get; set; }
|
||||||
|
public bool Successful { get; set; }
|
||||||
|
public int? Results { get; set; }
|
||||||
|
|
||||||
public IndexerQueryEvent(int indexerId, string query)
|
public IndexerQueryEvent(int indexerId, SearchCriteriaBase query, long time, bool successful, int? results = null)
|
||||||
{
|
{
|
||||||
IndexerId = indexerId;
|
IndexerId = indexerId;
|
||||||
Query = query;
|
Query = query;
|
||||||
|
Time = time;
|
||||||
|
Successful = successful;
|
||||||
|
Results = results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Core.History;
|
using NzbDrone.Core.History;
|
||||||
using NzbDrone.Core.Languages;
|
|
||||||
using Prowlarr.Http.REST;
|
using Prowlarr.Http.REST;
|
||||||
|
|
||||||
namespace Prowlarr.Api.V1.History
|
namespace Prowlarr.Api.V1.History
|
||||||
{
|
{
|
||||||
public class HistoryResource : RestResource
|
public class HistoryResource : RestResource
|
||||||
{
|
{
|
||||||
public int MovieId { get; set; }
|
public int IndexerId { get; set; }
|
||||||
public string SourceTitle { get; set; }
|
|
||||||
public bool QualityCutoffNotMet { get; set; }
|
|
||||||
public DateTime Date { get; set; }
|
public DateTime Date { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
|
|
||||||
@@ -32,8 +29,7 @@ namespace Prowlarr.Api.V1.History
|
|||||||
{
|
{
|
||||||
Id = model.Id,
|
Id = model.Id,
|
||||||
|
|
||||||
MovieId = model.IndexerId,
|
IndexerId = model.IndexerId,
|
||||||
SourceTitle = model.SourceTitle,
|
|
||||||
|
|
||||||
//QualityCutoffNotMet
|
//QualityCutoffNotMet
|
||||||
Date = model.Date,
|
Date = model.Date,
|
||||||
|
Reference in New Issue
Block a user