mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
Search and History Improvements
This commit is contained in:
@@ -46,7 +46,7 @@ class IndexersSelectInputConnector extends Component {
|
|||||||
|
|
||||||
IndexersSelectInputConnector.propTypes = {
|
IndexersSelectInputConnector.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
indexerIds: PropTypes.number.isRequired,
|
indexerIds: PropTypes.number,
|
||||||
value: PropTypes.arrayOf(PropTypes.number).isRequired,
|
value: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
onChange: PropTypes.func.isRequired
|
onChange: PropTypes.func.isRequired
|
||||||
|
@@ -51,6 +51,12 @@
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actionMenu {
|
||||||
|
&:hover {
|
||||||
|
color: #515253;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.translate {
|
.translate {
|
||||||
composes: link from '~Components/Link/Link.css';
|
composes: link from '~Components/Link/Link.css';
|
||||||
|
|
||||||
@@ -63,7 +69,7 @@
|
|||||||
line-height: 60px;
|
line-height: 60px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $toobarButtonHoverColor;
|
color: #515253;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -158,8 +158,7 @@ MovieEditorFooter.propTypes = {
|
|||||||
saveError: PropTypes.object,
|
saveError: PropTypes.object,
|
||||||
isDeleting: PropTypes.bool.isRequired,
|
isDeleting: PropTypes.bool.isRequired,
|
||||||
deleteError: PropTypes.object,
|
deleteError: PropTypes.object,
|
||||||
onSaveSelected: PropTypes.func.isRequired,
|
onSaveSelected: PropTypes.func.isRequired
|
||||||
onOrganizeMoviePress: PropTypes.func.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieEditorFooter;
|
export default MovieEditorFooter;
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
.actions {
|
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
min-width: 70px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sourceTitle {
|
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
@@ -1,162 +0,0 @@
|
|||||||
import HistoryDetailsModal from 'Activity/History/Details/HistoryDetailsModal';
|
|
||||||
import HistoryEventTypeCell from 'Activity/History/HistoryEventTypeCell';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import IconButton from 'Components/Link/IconButton';
|
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
|
||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
|
||||||
import TableRow from 'Components/Table/TableRow';
|
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
|
||||||
import MovieLanguage from 'Indexer/MovieLanguage';
|
|
||||||
import MovieQuality from 'Indexer/MovieQuality';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './MovieHistoryRow.css';
|
|
||||||
|
|
||||||
class MovieHistoryRow extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isMarkAsFailedModalOpen: false,
|
|
||||||
isDetailsModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onMarkAsFailedPress = () => {
|
|
||||||
this.setState({ isMarkAsFailedModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
onConfirmMarkAsFailed = () => {
|
|
||||||
this.props.onMarkAsFailedPress(this.props.id);
|
|
||||||
this.setState({ isMarkAsFailedModalOpen: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
onMarkAsFailedModalClose = () => {
|
|
||||||
this.setState({ isMarkAsFailedModalOpen: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
onDetailsPress = () => {
|
|
||||||
this.setState({ isDetailsModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
onDetailsModalClose = () => {
|
|
||||||
this.setState({ isDetailsModalOpen: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
eventType,
|
|
||||||
sourceTitle,
|
|
||||||
quality,
|
|
||||||
languages,
|
|
||||||
qualityCutoffNotMet,
|
|
||||||
date,
|
|
||||||
data,
|
|
||||||
isMarkingAsFailed,
|
|
||||||
shortDateFormat,
|
|
||||||
timeFormat,
|
|
||||||
onMarkAsFailedPress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
isMarkAsFailedModalOpen
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow>
|
|
||||||
<HistoryEventTypeCell
|
|
||||||
eventType={eventType}
|
|
||||||
data={data}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TableRowCell className={styles.sourceTitle}>
|
|
||||||
{sourceTitle}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
<MovieLanguage
|
|
||||||
languages={languages}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
<MovieQuality
|
|
||||||
quality={quality}
|
|
||||||
isCutoffNotMet={qualityCutoffNotMet}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<RelativeDateCellConnector
|
|
||||||
date={date}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TableRowCell className={styles.actions}>
|
|
||||||
<IconButton
|
|
||||||
name={icons.INFO}
|
|
||||||
onPress={this.onDetailsPress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
|
||||||
eventType === 'grabbed' &&
|
|
||||||
<IconButton
|
|
||||||
title={translate('MarkAsFailed')}
|
|
||||||
name={icons.REMOVE}
|
|
||||||
onPress={this.onMarkAsFailedPress}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={isMarkAsFailedModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title={translate('MarkAsFailed')}
|
|
||||||
message={translate('MarkAsFailedMessageText', [sourceTitle])}
|
|
||||||
confirmLabel={translate('MarkAsFailed')}
|
|
||||||
onConfirm={this.onConfirmMarkAsFailed}
|
|
||||||
onCancel={this.onMarkAsFailedModalClose}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<HistoryDetailsModal
|
|
||||||
isOpen={this.state.isDetailsModalOpen}
|
|
||||||
eventType={eventType}
|
|
||||||
sourceTitle={sourceTitle}
|
|
||||||
data={data}
|
|
||||||
isMarkingAsFailed={isMarkingAsFailed}
|
|
||||||
shortDateFormat={shortDateFormat}
|
|
||||||
timeFormat={timeFormat}
|
|
||||||
onMarkAsFailedPress={onMarkAsFailedPress}
|
|
||||||
onModalClose={this.onDetailsModalClose}
|
|
||||||
/>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieHistoryRow.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
eventType: PropTypes.string.isRequired,
|
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
|
||||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
quality: PropTypes.object.isRequired,
|
|
||||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
|
||||||
date: PropTypes.string.isRequired,
|
|
||||||
data: PropTypes.object.isRequired,
|
|
||||||
isMarkingAsFailed: PropTypes.bool,
|
|
||||||
movie: PropTypes.object.isRequired,
|
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
|
||||||
timeFormat: PropTypes.string.isRequired,
|
|
||||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieHistoryRow;
|
|
@@ -1,27 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { fetchHistory, markAsFailed } from 'Store/Actions/historyActions';
|
|
||||||
import createIndexerSelector from 'Store/Selectors/createIndexerSelector';
|
|
||||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
|
||||||
import MovieHistoryRow from './MovieHistoryRow';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createIndexerSelector(),
|
|
||||||
createUISettingsSelector(),
|
|
||||||
(movie, uiSettings) => {
|
|
||||||
return {
|
|
||||||
movie,
|
|
||||||
shortDateFormat: uiSettings.shortDateFormat,
|
|
||||||
timeFormat: uiSettings.timeFormat
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
fetchHistory,
|
|
||||||
markAsFailed
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(MovieHistoryRow);
|
|
@@ -1,19 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import MovieHistoryTableContentConnector from './MovieHistoryTableContentConnector';
|
|
||||||
|
|
||||||
function MovieHistoryTable(props) {
|
|
||||||
const {
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MovieHistoryTableContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieHistoryTable.propTypes = {
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieHistoryTable;
|
|
@@ -1,5 +0,0 @@
|
|||||||
.blankpad {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-left: 2em;
|
|
||||||
}
|
|
@@ -1,114 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import IconButton from 'Components/Link/IconButton';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|
||||||
import Table from 'Components/Table/Table';
|
|
||||||
import TableBody from 'Components/Table/TableBody';
|
|
||||||
import { icons } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import MovieHistoryRowConnector from './MovieHistoryRowConnector';
|
|
||||||
import styles from './MovieHistoryTableContent.css';
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'eventType',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sourceTitle',
|
|
||||||
label: translate('SourceTitle'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'languages',
|
|
||||||
label: translate('Languages'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'quality',
|
|
||||||
label: translate('Quality'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'customFormats',
|
|
||||||
label: translate('CustomFormats'),
|
|
||||||
isSortable: false,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'date',
|
|
||||||
label: translate('Date'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'actions',
|
|
||||||
label: React.createElement(IconButton, { name: icons.ADVANCED_SETTINGS }),
|
|
||||||
isVisible: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
class MovieHistoryTableContent extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
isPopulated,
|
|
||||||
error,
|
|
||||||
items,
|
|
||||||
onMarkAsFailedPress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const hasItems = !!items.length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
isFetching &&
|
|
||||||
<LoadingIndicator />
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isFetching && !!error &&
|
|
||||||
<div className={styles.blankpad}>Unable to load history</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
isPopulated && !hasItems && !error &&
|
|
||||||
<div className={styles.blankpad}>No history</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
isPopulated && hasItems && !error &&
|
|
||||||
<Table columns={columns}>
|
|
||||||
<TableBody>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<MovieHistoryRowConnector
|
|
||||||
key={item.id}
|
|
||||||
{...item}
|
|
||||||
onMarkAsFailedPress={onMarkAsFailedPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieHistoryTableContent.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieHistoryTableContent;
|
|
@@ -1,55 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { movieHistoryMarkAsFailed } from 'Store/Actions/movieHistoryActions';
|
|
||||||
import MovieHistoryTableContent from './MovieHistoryTableContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.movieHistory,
|
|
||||||
(movieHistory) => {
|
|
||||||
return movieHistory;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
movieHistoryMarkAsFailed
|
|
||||||
};
|
|
||||||
|
|
||||||
class MovieHistoryTableContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onMarkAsFailedPress = (historyId) => {
|
|
||||||
const {
|
|
||||||
movieId
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.props.movieHistoryMarkAsFailed({
|
|
||||||
historyId,
|
|
||||||
movieId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<MovieHistoryTableContent
|
|
||||||
{...this.props}
|
|
||||||
onMarkAsFailedPress={this.onMarkAsFailedPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieHistoryTableContentConnector.propTypes = {
|
|
||||||
movieId: PropTypes.number.isRequired,
|
|
||||||
movieHistoryMarkAsFailed: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(MovieHistoryTableContentConnector);
|
|
@@ -15,11 +15,11 @@ function createMapStateToProps() {
|
|||||||
createIndexerClientSideCollectionItemsSelector('indexerIndex'),
|
createIndexerClientSideCollectionItemsSelector('indexerIndex'),
|
||||||
createDimensionsSelector(),
|
createDimensionsSelector(),
|
||||||
(
|
(
|
||||||
movies,
|
indexers,
|
||||||
dimensionsState
|
dimensionsState
|
||||||
) => {
|
) => {
|
||||||
return {
|
return {
|
||||||
...movies,
|
...indexers,
|
||||||
isSmallScreen: dimensionsState.isSmallScreen
|
isSmallScreen: dimensionsState.isSmallScreen
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ function SearchIndexSortMenu(props) {
|
|||||||
</SortMenuItem>
|
</SortMenuItem>
|
||||||
|
|
||||||
<SortMenuItem
|
<SortMenuItem
|
||||||
name="sortTitle"
|
name="title"
|
||||||
sortKey={sortKey}
|
sortKey={sortKey}
|
||||||
sortDirection={sortDirection}
|
sortDirection={sortDirection}
|
||||||
onPress={onSortSelect}
|
onPress={onSortSelect}
|
||||||
|
@@ -4,6 +4,7 @@ import IndexersSelectInputConnector from 'Components/Form/IndexersSelectInputCon
|
|||||||
import TextInput from 'Components/Form/TextInput';
|
import TextInput from 'Components/Form/TextInput';
|
||||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||||
import PageContentFooter from 'Components/Page/PageContentFooter';
|
import PageContentFooter from 'Components/Page/PageContentFooter';
|
||||||
|
import SearchFooterLabel from './SearchFooterLabel';
|
||||||
import styles from './SearchFooter.css';
|
import styles from './SearchFooter.css';
|
||||||
|
|
||||||
class SearchFooter extends Component {
|
class SearchFooter extends Component {
|
||||||
@@ -61,9 +62,13 @@ class SearchFooter extends Component {
|
|||||||
return (
|
return (
|
||||||
<PageContentFooter>
|
<PageContentFooter>
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
|
<SearchFooterLabel
|
||||||
|
label={'Query'}
|
||||||
|
isSaving={false}
|
||||||
|
/>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
name='searchQuery'
|
name='searchQuery'
|
||||||
placeholder='Query'
|
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
isDisabled={isFetching}
|
isDisabled={isFetching}
|
||||||
onChange={this.onInputChange}
|
onChange={this.onInputChange}
|
||||||
@@ -71,6 +76,11 @@ class SearchFooter extends Component {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.indexerContainer}>
|
<div className={styles.indexerContainer}>
|
||||||
|
<SearchFooterLabel
|
||||||
|
label={'Indexers'}
|
||||||
|
isSaving={false}
|
||||||
|
/>
|
||||||
|
|
||||||
<IndexersSelectInputConnector
|
<IndexersSelectInputConnector
|
||||||
name='indexerIds'
|
name='indexerIds'
|
||||||
placeholder='Indexers'
|
placeholder='Indexers'
|
||||||
|
8
frontend/src/Search/SearchFooterLabel.css
Normal file
8
frontend/src/Search/SearchFooterLabel.css
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.label {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.savingIcon {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
40
frontend/src/Search/SearchFooterLabel.js
Normal file
40
frontend/src/Search/SearchFooterLabel.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import SpinnerIcon from 'Components/SpinnerIcon';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import styles from './SearchFooterLabel.css';
|
||||||
|
|
||||||
|
function SearchFooterLabel(props) {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
label,
|
||||||
|
isSaving
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
{label}
|
||||||
|
|
||||||
|
{
|
||||||
|
isSaving &&
|
||||||
|
<SpinnerIcon
|
||||||
|
className={styles.savingIcon}
|
||||||
|
name={icons.SPINNER}
|
||||||
|
isSpinning={true}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchFooterLabel.propTypes = {
|
||||||
|
className: PropTypes.string.isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
isSaving: PropTypes.bool.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
SearchFooterLabel.defaultProps = {
|
||||||
|
className: styles.label
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SearchFooterLabel;
|
@@ -11,6 +11,7 @@ import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
|||||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import { align, icons, sortDirections } from 'Helpers/Props';
|
import { align, icons, sortDirections } from 'Helpers/Props';
|
||||||
|
import NoIndexer from 'Indexer/NoIndexer';
|
||||||
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
||||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||||
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
||||||
@@ -83,7 +84,7 @@ class SearchIndex extends Component {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// Reset if not sorting by sortTitle
|
// Reset if not sorting by sortTitle
|
||||||
if (sortKey !== 'sortTitle') {
|
if (sortKey !== 'title') {
|
||||||
this.setState({ jumpBarItems: { order: [] } });
|
this.setState({ jumpBarItems: { order: [] } });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -161,6 +162,7 @@ class SearchIndex extends Component {
|
|||||||
onScroll,
|
onScroll,
|
||||||
onSortSelect,
|
onSortSelect,
|
||||||
onFilterSelect,
|
onFilterSelect,
|
||||||
|
hasIndexers,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -174,8 +176,6 @@ class SearchIndex extends Component {
|
|||||||
const isLoaded = !!(!error && isPopulated && items.length && scroller);
|
const isLoaded = !!(!error && isPopulated && items.length && scroller);
|
||||||
const hasNoIndexer = !totalItems;
|
const hasNoIndexer = !totalItems;
|
||||||
|
|
||||||
console.log(hasNoIndexer);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent>
|
<PageContent>
|
||||||
<PageToolbar>
|
<PageToolbar>
|
||||||
@@ -247,7 +247,12 @@ class SearchIndex extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!error && !isFetching && !items.length &&
|
!error && !isFetching && !hasIndexers &&
|
||||||
|
<NoIndexer />
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
!error && !isFetching && hasIndexers && !items.length &&
|
||||||
<NoSearchResults totalItems={totalItems} />
|
<NoSearchResults totalItems={totalItems} />
|
||||||
}
|
}
|
||||||
</PageContentBody>
|
</PageContentBody>
|
||||||
@@ -286,7 +291,8 @@ SearchIndex.propTypes = {
|
|||||||
onSortSelect: PropTypes.func.isRequired,
|
onSortSelect: PropTypes.func.isRequired,
|
||||||
onFilterSelect: PropTypes.func.isRequired,
|
onFilterSelect: PropTypes.func.isRequired,
|
||||||
onSearchPress: PropTypes.func.isRequired,
|
onSearchPress: PropTypes.func.isRequired,
|
||||||
onScroll: PropTypes.func.isRequired
|
onScroll: PropTypes.func.isRequired,
|
||||||
|
hasIndexers: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SearchIndex;
|
export default SearchIndex;
|
||||||
|
@@ -6,19 +6,23 @@ import withScrollPosition from 'Components/withScrollPosition';
|
|||||||
import { fetchReleases, setReleasesFilter, setReleasesSort, setReleasesTableOption } from 'Store/Actions/releaseActions';
|
import { fetchReleases, setReleasesFilter, setReleasesSort, setReleasesTableOption } from 'Store/Actions/releaseActions';
|
||||||
import scrollPositions from 'Store/scrollPositions';
|
import scrollPositions from 'Store/scrollPositions';
|
||||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||||
|
import createIndexerClientSideCollectionItemsSelector from 'Store/Selectors/createIndexerClientSideCollectionItemsSelector';
|
||||||
import createReleaseClientSideCollectionItemsSelector from 'Store/Selectors/createReleaseClientSideCollectionItemsSelector';
|
import createReleaseClientSideCollectionItemsSelector from 'Store/Selectors/createReleaseClientSideCollectionItemsSelector';
|
||||||
import SearchIndex from './SearchIndex';
|
import SearchIndex from './SearchIndex';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
|
createIndexerClientSideCollectionItemsSelector('indexerIndex'),
|
||||||
createReleaseClientSideCollectionItemsSelector('releases'),
|
createReleaseClientSideCollectionItemsSelector('releases'),
|
||||||
createDimensionsSelector(),
|
createDimensionsSelector(),
|
||||||
(
|
(
|
||||||
movies,
|
indexers,
|
||||||
|
releases,
|
||||||
dimensionsState
|
dimensionsState
|
||||||
) => {
|
) => {
|
||||||
return {
|
return {
|
||||||
...movies,
|
...releases,
|
||||||
|
hasIndexers: indexers.items.length > 0,
|
||||||
isSmallScreen: dimensionsState.isSmallScreen
|
isSmallScreen: dimensionsState.isSmallScreen
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
.status {
|
.protocol {
|
||||||
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
|
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
|
||||||
|
|
||||||
flex: 0 0 60px;
|
flex: 0 0 60px;
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.protocol {
|
||||||
composes: cell;
|
composes: cell;
|
||||||
|
|
||||||
flex: 0 0 60px;
|
flex: 0 0 60px;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { setMovieSort } from 'Store/Actions/indexerIndexActions';
|
import { setReleasesSort } from 'Store/Actions/releaseActions';
|
||||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
import SearchIndexTable from './SearchIndexTable';
|
import SearchIndexTable from './SearchIndexTable';
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ function createMapStateToProps() {
|
|||||||
function createMapDispatchToProps(dispatch, props) {
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
return {
|
return {
|
||||||
onSortPress(sortKey) {
|
onSortPress(sortKey) {
|
||||||
dispatch(setMovieSort({ sortKey }));
|
dispatch(setReleasesSort({ sortKey }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ class AddIndexerItem extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
name,
|
||||||
implementation,
|
implementation,
|
||||||
implementationName,
|
implementationName,
|
||||||
infoLink,
|
infoLink,
|
||||||
@@ -47,7 +48,7 @@ class AddIndexerItem extends Component {
|
|||||||
|
|
||||||
<div className={styles.overlay}>
|
<div className={styles.overlay}>
|
||||||
<div className={styles.name}>
|
<div className={styles.name}>
|
||||||
{implementationName}
|
{name}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.actions}>
|
<div className={styles.actions}>
|
||||||
@@ -101,6 +102,7 @@ class AddIndexerItem extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AddIndexerItem.propTypes = {
|
AddIndexerItem.propTypes = {
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
implementation: PropTypes.string.isRequired,
|
implementation: PropTypes.string.isRequired,
|
||||||
implementationName: PropTypes.string.isRequired,
|
implementationName: PropTypes.string.isRequired,
|
||||||
infoLink: PropTypes.string.isRequired,
|
infoLink: PropTypes.string.isRequired,
|
||||||
|
@@ -88,33 +88,6 @@ class UISettings extends Component {
|
|||||||
id="uiSettings"
|
id="uiSettings"
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
<FieldSet legend={translate('Calendar')}>
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('SettingsFirstDayOfWeek')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.SELECT}
|
|
||||||
name="firstDayOfWeek"
|
|
||||||
values={firstDayOfWeekOptions}
|
|
||||||
onChange={onInputChange}
|
|
||||||
{...settings.firstDayOfWeek}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('SettingsWeekColumnHeader')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.SELECT}
|
|
||||||
name="calendarWeekColumnHeader"
|
|
||||||
values={weekColumnOptions}
|
|
||||||
onChange={onInputChange}
|
|
||||||
helpText={translate('SettingsWeekColumnHeaderHelpText')}
|
|
||||||
{...settings.calendarWeekColumnHeader}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</FieldSet>
|
|
||||||
|
|
||||||
<FieldSet legend={translate('Dates')}>
|
<FieldSet legend={translate('Dates')}>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('SettingsShortDateFormat')}</FormLabel>
|
<FormLabel>{translate('SettingsShortDateFormat')}</FormLabel>
|
||||||
|
@@ -60,25 +60,25 @@ export const defaultState = {
|
|||||||
name: 'protocol',
|
name: 'protocol',
|
||||||
label: translate('Protocol'),
|
label: translate('Protocol'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: false
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'privacy',
|
name: 'privacy',
|
||||||
label: translate('Privacy'),
|
label: translate('Privacy'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: false
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'added',
|
name: 'added',
|
||||||
label: translate('Added'),
|
label: translate('Added'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: false
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'capabilities',
|
name: 'capabilities',
|
||||||
label: 'Capabilities',
|
label: 'Categories',
|
||||||
isSortable: false,
|
isSortable: false,
|
||||||
isVisible: false
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'tags',
|
name: 'tags',
|
||||||
|
@@ -26,7 +26,7 @@ export const defaultState = {
|
|||||||
isPopulated: false,
|
isPopulated: false,
|
||||||
error: null,
|
error: null,
|
||||||
items: [],
|
items: [],
|
||||||
sortKey: 'releaseWeight',
|
sortKey: 'title',
|
||||||
sortDirection: sortDirections.ASCENDING,
|
sortDirection: sortDirections.ASCENDING,
|
||||||
|
|
||||||
columns: [
|
columns: [
|
||||||
|
@@ -113,8 +113,8 @@ module.exports = {
|
|||||||
//
|
//
|
||||||
// Toolbar
|
// Toolbar
|
||||||
|
|
||||||
toobarButtonHoverColor: '#ffc230',
|
toobarButtonHoverColor: '#e66000',
|
||||||
toobarButtonSelectedColor: '#ffc230',
|
toobarButtonSelectedColor: '#e66000',
|
||||||
|
|
||||||
//
|
//
|
||||||
// Scroller
|
// Scroller
|
||||||
|
@@ -17,7 +17,7 @@ class MoreInfo extends Component {
|
|||||||
<DescriptionList>
|
<DescriptionList>
|
||||||
<DescriptionListItemTitle>Home page</DescriptionListItemTitle>
|
<DescriptionListItemTitle>Home page</DescriptionListItemTitle>
|
||||||
<DescriptionListItemDescription>
|
<DescriptionListItemDescription>
|
||||||
<Link to="https://prowlarr.video/">prowlarr.video</Link>
|
<Link to="https://prowlarr.com/">prowlarr.com</Link>
|
||||||
</DescriptionListItemDescription>
|
</DescriptionListItemDescription>
|
||||||
|
|
||||||
<DescriptionListItemTitle>Discord</DescriptionListItemTitle>
|
<DescriptionListItemTitle>Discord</DescriptionListItemTitle>
|
||||||
|
@@ -13,11 +13,10 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
|
|||||||
private static readonly Regex BeginningThe = new Regex(@"^the\s", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
private static readonly Regex BeginningThe = new Regex(@"^the\s", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
|
||||||
public List<string> SceneTitles { get; set; }
|
public List<string> SceneTitles { get; set; }
|
||||||
public virtual bool UserInvokedSearch { get; set; }
|
|
||||||
public virtual bool InteractiveSearch { get; set; }
|
public virtual bool InteractiveSearch { get; set; }
|
||||||
|
public List<int> IndexerIds { get; set; }
|
||||||
public string ImdbId { get; set; }
|
public string ImdbId { get; set; }
|
||||||
public int TmdbId { get; set; }
|
public int TmdbId { get; set; }
|
||||||
public List<int> IndexerIds { get; set; }
|
|
||||||
|
|
||||||
public List<string> QueryTitles => SceneTitles.Select(GetQueryTitle).ToList();
|
public List<string> QueryTitles => SceneTitles.Select(GetQueryTitle).ToList();
|
||||||
|
|
||||||
|
@@ -15,8 +15,8 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
{
|
{
|
||||||
public interface ISearchForNzb
|
public interface ISearchForNzb
|
||||||
{
|
{
|
||||||
List<ReleaseInfo> Search(string query, List<int> indexerIds, bool userInvokedSearch, bool interactiveSearch);
|
List<ReleaseInfo> Search(string query, List<int> indexerIds, bool interactiveSearch);
|
||||||
NewznabResults Search(NewznabRequest request, List<int> indexerIds, bool userInvokedSearch, bool interactiveSearch);
|
NewznabResults Search(NewznabRequest request, List<int> indexerIds, bool interactiveSearch);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NzbSearchService : ISearchForNzb
|
public class NzbSearchService : ISearchForNzb
|
||||||
@@ -34,26 +34,25 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ReleaseInfo> Search(string query, List<int> indexerIds, bool userInvokedSearch, bool interactiveSearch)
|
public List<ReleaseInfo> Search(string query, List<int> indexerIds, bool interactiveSearch)
|
||||||
{
|
{
|
||||||
var searchSpec = Get<MovieSearchCriteria>(query, indexerIds, userInvokedSearch, interactiveSearch);
|
var searchSpec = Get<MovieSearchCriteria>(query, indexerIds, interactiveSearch);
|
||||||
|
|
||||||
return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
|
return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NewznabResults Search(NewznabRequest request, List<int> indexerIds, bool userInvokedSearch, bool interactiveSearch)
|
public NewznabResults Search(NewznabRequest request, List<int> indexerIds, bool interactiveSearch)
|
||||||
{
|
{
|
||||||
var searchSpec = Get<MovieSearchCriteria>(request.q, indexerIds, userInvokedSearch, interactiveSearch);
|
var searchSpec = Get<MovieSearchCriteria>(request.q, indexerIds, interactiveSearch);
|
||||||
|
|
||||||
return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) };
|
return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) };
|
||||||
}
|
}
|
||||||
|
|
||||||
private TSpec Get<TSpec>(string query, List<int> indexerIds, bool userInvokedSearch, bool interactiveSearch)
|
private TSpec Get<TSpec>(string query, List<int> indexerIds, bool interactiveSearch)
|
||||||
where TSpec : SearchCriteriaBase, new()
|
where TSpec : SearchCriteriaBase, new()
|
||||||
{
|
{
|
||||||
var spec = new TSpec()
|
var spec = new TSpec()
|
||||||
{
|
{
|
||||||
UserInvokedSearch = userInvokedSearch,
|
|
||||||
InteractiveSearch = interactiveSearch
|
InteractiveSearch = interactiveSearch
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -53,10 +53,10 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||||||
yield return GetDefinition("Nzb-Tortuga", GetSettings("https://www.nzb-tortuga.com"));
|
yield return GetDefinition("Nzb-Tortuga", GetSettings("https://www.nzb-tortuga.com"));
|
||||||
yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su"));
|
yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su"));
|
||||||
yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat"));
|
yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat"));
|
||||||
yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws", categories: new[] { 2030, 2040, 2045, 2050, 2060, 2070, 2080, 2090 }));
|
yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws"));
|
||||||
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"));
|
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"));
|
||||||
yield return GetDefinition("nzbplanet.net", GetSettings("https://api.nzbplanet.net"));
|
yield return GetDefinition("nzbplanet.net", GetSettings("https://api.nzbplanet.net"));
|
||||||
yield return GetDefinition("omgwtfnzbs", GetSettings("https://api.omgwtfnzbs.me", categories: new[] { 2000, 2020, 2030, 2040, 2045, 2050, 2070 }));
|
yield return GetDefinition("omgwtfnzbs", GetSettings("https://api.omgwtfnzbs.me"));
|
||||||
yield return GetDefinition("OZnzb.com", GetSettings("https://api.oznzb.com"));
|
yield return GetDefinition("OZnzb.com", GetSettings("https://api.oznzb.com"));
|
||||||
yield return GetDefinition("SimplyNZBs", GetSettings("https://simplynzbs.com"));
|
yield return GetDefinition("SimplyNZBs", GetSettings("https://simplynzbs.com"));
|
||||||
yield return GetDefinition("Tabula Rasa", GetSettings("https://www.tabula-rasa.pw", apiPath: @"/api/v1/api"));
|
yield return GetDefinition("Tabula Rasa", GetSettings("https://www.tabula-rasa.pw", apiPath: @"/api/v1/api"));
|
||||||
|
@@ -19,6 +19,27 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
|||||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||||
|
|
||||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
|
||||||
|
|
||||||
|
public override IndexerCapabilities Capabilities => new IndexerCapabilities
|
||||||
|
{
|
||||||
|
TvSearchParams = new List<TvSearchParam>
|
||||||
|
{
|
||||||
|
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||||
|
},
|
||||||
|
MovieSearchParams = new List<MovieSearchParam>
|
||||||
|
{
|
||||||
|
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||||
|
},
|
||||||
|
MusicSearchParams = new List<MusicSearchParam>
|
||||||
|
{
|
||||||
|
MusicSearchParam.Q
|
||||||
|
},
|
||||||
|
BookSearchParams = new List<BookSearchParam>
|
||||||
|
{
|
||||||
|
BookSearchParam.Q
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public override TimeSpan RateLimit => TimeSpan.FromSeconds(2);
|
public override TimeSpan RateLimit => TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
public Rarbg(IRarbgTokenProvider tokenProvider, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
public Rarbg(IRarbgTokenProvider tokenProvider, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.113.0-0" />
|
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.113.0-0" />
|
||||||
<PackageReference Include="System.Text.Json" Version="4.7.2" />
|
<PackageReference Include="System.Text.Json" Version="4.7.2" />
|
||||||
<PackageReference Include="MonoTorrent" Version="1.0.19" />
|
<PackageReference Include="MonoTorrent" Version="1.0.19" />
|
||||||
|
<PackageReference Include="YamlDotNet" Version="8.1.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\NzbDrone.Common\Prowlarr.Common.csproj" />
|
<ProjectReference Include="..\NzbDrone.Common\Prowlarr.Common.csproj" />
|
||||||
|
@@ -20,7 +20,7 @@ namespace Prowlarr.Api.V1.History
|
|||||||
GetResourcePaged = GetHistory;
|
GetResourcePaged = GetHistory;
|
||||||
|
|
||||||
Get("/since", x => GetHistorySince());
|
Get("/since", x => GetHistorySince());
|
||||||
Get("/movie", x => GetMovieHistory());
|
Get("/indexer", x => GetIndexerHistory());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HistoryResource MapToResource(NzbDrone.Core.History.History model, bool includeMovie)
|
protected HistoryResource MapToResource(NzbDrone.Core.History.History model, bool includeMovie)
|
||||||
@@ -75,26 +75,26 @@ namespace Prowlarr.Api.V1.History
|
|||||||
return _historyService.Since(date, eventType).Select(h => MapToResource(h, includeMovie)).ToList();
|
return _historyService.Since(date, eventType).Select(h => MapToResource(h, includeMovie)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<HistoryResource> GetMovieHistory()
|
private List<HistoryResource> GetIndexerHistory()
|
||||||
{
|
{
|
||||||
var queryMovieId = Request.Query.MovieId;
|
var queryIndexerId = Request.Query.IndexerId;
|
||||||
var queryEventType = Request.Query.EventType;
|
var queryEventType = Request.Query.EventType;
|
||||||
|
|
||||||
if (!queryMovieId.HasValue)
|
if (!queryIndexerId.HasValue)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("movieId is missing");
|
throw new BadRequestException("indexerId is missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
int movieId = Convert.ToInt32(queryMovieId.Value);
|
int indexerId = Convert.ToInt32(queryIndexerId.Value);
|
||||||
HistoryEventType? eventType = null;
|
HistoryEventType? eventType = null;
|
||||||
var includeMovie = Request.GetBooleanQueryParameter("includeMovie");
|
var includeIndexer = Request.GetBooleanQueryParameter("includeIndexer");
|
||||||
|
|
||||||
if (queryEventType.HasValue)
|
if (queryEventType.HasValue)
|
||||||
{
|
{
|
||||||
eventType = (HistoryEventType)Convert.ToInt32(queryEventType.Value);
|
eventType = (HistoryEventType)Convert.ToInt32(queryEventType.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _historyService.GetByIndexerId(movieId, eventType).Select(h => MapToResource(h, includeMovie)).ToList();
|
return _historyService.GetByIndexerId(indexerId, eventType).Select(h => MapToResource(h, includeIndexer)).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -62,8 +62,10 @@ namespace Prowlarr.Api.V1.Indexers
|
|||||||
Response response = indexerInstance.GetCapabilities().ToXml();
|
Response response = indexerInstance.GetCapabilities().ToXml();
|
||||||
response.ContentType = "application/rss+xml";
|
response.ContentType = "application/rss+xml";
|
||||||
return response;
|
return response;
|
||||||
|
case "tvsearch":
|
||||||
|
case "music":
|
||||||
case "movie":
|
case "movie":
|
||||||
Response movieResponse = _nzbSearchService.Search(request, new List<int> { indexer.Id }, true, false).ToXml();
|
Response movieResponse = _nzbSearchService.Search(request, new List<int> { indexer.Id }, false).ToXml();
|
||||||
movieResponse.ContentType = "application/rss+xml";
|
movieResponse.ContentType = "application/rss+xml";
|
||||||
return movieResponse;
|
return movieResponse;
|
||||||
default:
|
default:
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Nancy.ModelBinding;
|
using Nancy.ModelBinding;
|
||||||
using NLog;
|
using NLog;
|
||||||
@@ -50,7 +49,7 @@ namespace Prowlarr.Api.V1.Search
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var decisions = _nzbSearhService.Search(query, indexerIds, true, true);
|
var decisions = _nzbSearhService.Search(query, indexerIds, true);
|
||||||
|
|
||||||
return MapDecisions(decisions);
|
return MapDecisions(decisions);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user