Search and History Improvements

pull/2/head
Qstick 4 years ago
parent dd8313f9ce
commit 94b295ddcf

@ -46,7 +46,7 @@ class IndexersSelectInputConnector extends Component {
IndexersSelectInputConnector.propTypes = {
name: PropTypes.string.isRequired,
indexerIds: PropTypes.number.isRequired,
indexerIds: PropTypes.number,
value: PropTypes.arrayOf(PropTypes.number).isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
onChange: PropTypes.func.isRequired

@ -51,6 +51,12 @@
flex-grow: 1;
}
.actionMenu {
&:hover {
color: #515253;
}
}
.translate {
composes: link from '~Components/Link/Link.css';
@ -63,7 +69,7 @@
line-height: 60px;
&:hover {
color: $toobarButtonHoverColor;
color: #515253;
}
}

@ -158,8 +158,7 @@ MovieEditorFooter.propTypes = {
saveError: PropTypes.object,
isDeleting: PropTypes.bool.isRequired,
deleteError: PropTypes.object,
onSaveSelected: PropTypes.func.isRequired,
onOrganizeMoviePress: PropTypes.func.isRequired
onSaveSelected: PropTypes.func.isRequired
};
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'),
createDimensionsSelector(),
(
movies,
indexers,
dimensionsState
) => {
return {
...movies,
...indexers,
isSmallScreen: dimensionsState.isSmallScreen
};
}

@ -30,7 +30,7 @@ function SearchIndexSortMenu(props) {
</SortMenuItem>
<SortMenuItem
name="sortTitle"
name="title"
sortKey={sortKey}
sortDirection={sortDirection}
onPress={onSortSelect}

@ -4,6 +4,7 @@ import IndexersSelectInputConnector from 'Components/Form/IndexersSelectInputCon
import TextInput from 'Components/Form/TextInput';
import SpinnerButton from 'Components/Link/SpinnerButton';
import PageContentFooter from 'Components/Page/PageContentFooter';
import SearchFooterLabel from './SearchFooterLabel';
import styles from './SearchFooter.css';
class SearchFooter extends Component {
@ -61,9 +62,13 @@ class SearchFooter extends Component {
return (
<PageContentFooter>
<div className={styles.inputContainer}>
<SearchFooterLabel
label={'Query'}
isSaving={false}
/>
<TextInput
name='searchQuery'
placeholder='Query'
value={searchQuery}
isDisabled={isFetching}
onChange={this.onInputChange}
@ -71,6 +76,11 @@ class SearchFooter extends Component {
</div>
<div className={styles.indexerContainer}>
<SearchFooterLabel
label={'Indexers'}
isSaving={false}
/>
<IndexersSelectInputConnector
name='indexerIds'
placeholder='Indexers'

@ -0,0 +1,8 @@
.label {
margin-bottom: 3px;
font-weight: bold;
}
.savingIcon {
margin-left: 8px;
}

@ -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 TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import { align, icons, sortDirections } from 'Helpers/Props';
import NoIndexer from 'Indexer/NoIndexer';
import * as keyCodes from 'Utilities/Constants/keyCodes';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
@ -83,7 +84,7 @@ class SearchIndex extends Component {
} = this.props;
// Reset if not sorting by sortTitle
if (sortKey !== 'sortTitle') {
if (sortKey !== 'title') {
this.setState({ jumpBarItems: { order: [] } });
return;
}
@ -161,6 +162,7 @@ class SearchIndex extends Component {
onScroll,
onSortSelect,
onFilterSelect,
hasIndexers,
...otherProps
} = this.props;
@ -174,8 +176,6 @@ class SearchIndex extends Component {
const isLoaded = !!(!error && isPopulated && items.length && scroller);
const hasNoIndexer = !totalItems;
console.log(hasNoIndexer);
return (
<PageContent>
<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} />
}
</PageContentBody>
@ -286,7 +291,8 @@ SearchIndex.propTypes = {
onSortSelect: PropTypes.func.isRequired,
onFilterSelect: PropTypes.func.isRequired,
onSearchPress: PropTypes.func.isRequired,
onScroll: PropTypes.func.isRequired
onScroll: PropTypes.func.isRequired,
hasIndexers: PropTypes.func.isRequired
};
export default SearchIndex;

@ -6,19 +6,23 @@ import withScrollPosition from 'Components/withScrollPosition';
import { fetchReleases, setReleasesFilter, setReleasesSort, setReleasesTableOption } from 'Store/Actions/releaseActions';
import scrollPositions from 'Store/scrollPositions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createIndexerClientSideCollectionItemsSelector from 'Store/Selectors/createIndexerClientSideCollectionItemsSelector';
import createReleaseClientSideCollectionItemsSelector from 'Store/Selectors/createReleaseClientSideCollectionItemsSelector';
import SearchIndex from './SearchIndex';
function createMapStateToProps() {
return createSelector(
createIndexerClientSideCollectionItemsSelector('indexerIndex'),
createReleaseClientSideCollectionItemsSelector('releases'),
createDimensionsSelector(),
(
movies,
indexers,
releases,
dimensionsState
) => {
return {
...movies,
...releases,
hasIndexers: indexers.items.length > 0,
isSmallScreen: dimensionsState.isSmallScreen
};
}

@ -1,4 +1,4 @@
.status {
.protocol {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 60px;

@ -5,7 +5,7 @@
align-items: center;
}
.status {
.protocol {
composes: cell;
flex: 0 0 60px;

@ -1,6 +1,6 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { setMovieSort } from 'Store/Actions/indexerIndexActions';
import { setReleasesSort } from 'Store/Actions/releaseActions';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import SearchIndexTable from './SearchIndexTable';
@ -23,7 +23,7 @@ function createMapStateToProps() {
function createMapDispatchToProps(dispatch, props) {
return {
onSortPress(sortKey) {
dispatch(setMovieSort({ sortKey }));
dispatch(setReleasesSort({ sortKey }));
}
};
}

@ -27,6 +27,7 @@ class AddIndexerItem extends Component {
render() {
const {
name,
implementation,
implementationName,
infoLink,
@ -47,7 +48,7 @@ class AddIndexerItem extends Component {
<div className={styles.overlay}>
<div className={styles.name}>
{implementationName}
{name}
</div>
<div className={styles.actions}>
@ -101,6 +102,7 @@ class AddIndexerItem extends Component {
}
AddIndexerItem.propTypes = {
name: PropTypes.string.isRequired,
implementation: PropTypes.string.isRequired,
implementationName: PropTypes.string.isRequired,
infoLink: PropTypes.string.isRequired,

@ -88,33 +88,6 @@ class UISettings extends Component {
id="uiSettings"
{...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')}>
<FormGroup>
<FormLabel>{translate('SettingsShortDateFormat')}</FormLabel>

@ -60,25 +60,25 @@ export const defaultState = {
name: 'protocol',
label: translate('Protocol'),
isSortable: true,
isVisible: false
isVisible: true
},
{
name: 'privacy',
label: translate('Privacy'),
isSortable: true,
isVisible: false
isVisible: true
},
{
name: 'added',
label: translate('Added'),
isSortable: true,
isVisible: false
isVisible: true
},
{
name: 'capabilities',
label: 'Capabilities',
label: 'Categories',
isSortable: false,
isVisible: false
isVisible: true
},
{
name: 'tags',

@ -26,7 +26,7 @@ export const defaultState = {
isPopulated: false,
error: null,
items: [],
sortKey: 'releaseWeight',
sortKey: 'title',
sortDirection: sortDirections.ASCENDING,
columns: [

@ -113,8 +113,8 @@ module.exports = {
//
// Toolbar
toobarButtonHoverColor: '#ffc230',
toobarButtonSelectedColor: '#ffc230',
toobarButtonHoverColor: '#e66000',
toobarButtonSelectedColor: '#e66000',
//
// Scroller

@ -17,7 +17,7 @@ class MoreInfo extends Component {
<DescriptionList>
<DescriptionListItemTitle>Home page</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://prowlarr.video/">prowlarr.video</Link>
<Link to="https://prowlarr.com/">prowlarr.com</Link>
</DescriptionListItemDescription>
<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);
public List<string> SceneTitles { get; set; }
public virtual bool UserInvokedSearch { get; set; }
public virtual bool InteractiveSearch { get; set; }
public List<int> IndexerIds { get; set; }
public string ImdbId { get; set; }
public int TmdbId { get; set; }
public List<int> IndexerIds { get; set; }
public List<string> QueryTitles => SceneTitles.Select(GetQueryTitle).ToList();

@ -15,8 +15,8 @@ namespace NzbDrone.Core.IndexerSearch
{
public interface ISearchForNzb
{
List<ReleaseInfo> Search(string query, List<int> indexerIds, bool userInvokedSearch, bool interactiveSearch);
NewznabResults Search(NewznabRequest request, 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 interactiveSearch);
}
public class NzbSearchService : ISearchForNzb
@ -34,26 +34,25 @@ namespace NzbDrone.Core.IndexerSearch
_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);
}
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) };
}
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()
{
var spec = new TSpec()
{
UserInvokedSearch = userInvokedSearch,
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.su", GetSettings("https://api.nzb.su"));
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("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("SimplyNZBs", GetSettings("https://simplynzbs.com"));
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 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 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.Text.Json" Version="4.7.2" />
<PackageReference Include="MonoTorrent" Version="1.0.19" />
<PackageReference Include="YamlDotNet" Version="8.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NzbDrone.Common\Prowlarr.Common.csproj" />

@ -20,7 +20,7 @@ namespace Prowlarr.Api.V1.History
GetResourcePaged = GetHistory;
Get("/since", x => GetHistorySince());
Get("/movie", x => GetMovieHistory());
Get("/indexer", x => GetIndexerHistory());
}
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();
}
private List<HistoryResource> GetMovieHistory()
private List<HistoryResource> GetIndexerHistory()
{
var queryMovieId = Request.Query.MovieId;
var queryIndexerId = Request.Query.IndexerId;
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;
var includeMovie = Request.GetBooleanQueryParameter("includeMovie");
var includeIndexer = Request.GetBooleanQueryParameter("includeIndexer");
if (queryEventType.HasValue)
{
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.ContentType = "application/rss+xml";
return response;
case "tvsearch":
case "music":
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";
return movieResponse;
default:

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Nancy.ModelBinding;
using NLog;
@ -50,7 +49,7 @@ namespace Prowlarr.Api.V1.Search
{
try
{
var decisions = _nzbSearhService.Search(query, indexerIds, true, true);
var decisions = _nzbSearhService.Search(query, indexerIds, true);
return MapDecisions(decisions);
}

Loading…
Cancel
Save