diff --git a/frontend/src/Activity/Queue/Queue.js b/frontend/src/Activity/Queue/Queue.js
index 0b5827ea9..638fb1f52 100644
--- a/frontend/src/Activity/Queue/Queue.js
+++ b/frontend/src/Activity/Queue/Queue.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
+import FilterMenu from 'Components/Menu/FilterMenu';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
@@ -21,6 +22,7 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
+import QueueFilterModal from './QueueFilterModal';
import QueueOptionsConnector from './QueueOptionsConnector';
import QueueRowConnector from './QueueRowConnector';
import RemoveQueueItemsModal from './RemoveQueueItemsModal';
@@ -151,11 +153,16 @@ class Queue extends Component {
isEpisodesPopulated,
episodesError,
columns,
+ selectedFilterKey,
+ filters,
+ customFilters,
+ count,
totalRecords,
isGrabbing,
isRemoving,
isRefreshMonitoredDownloadsExecuting,
onRefreshPress,
+ onFilterSelect,
...otherProps
} = this.props;
@@ -218,6 +225,15 @@ class Queue extends Component {
iconName={icons.TABLE}
/>
+
+
@@ -239,7 +255,11 @@ class Queue extends Component {
{
isAllPopulated && !hasError && !items.length ?
- {translate('QueueIsEmpty')}
+ {
+ selectedFilterKey !== 'all' && count > 0 ?
+ translate('QueueFilterHasNoItems') :
+ translate('QueueIsEmpty')
+ }
:
null
}
@@ -323,13 +343,18 @@ Queue.propTypes = {
isEpisodesPopulated: PropTypes.bool.isRequired,
episodesError: PropTypes.object,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
+ selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ filters: PropTypes.arrayOf(PropTypes.object).isRequired,
+ customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
+ count: PropTypes.number.isRequired,
totalRecords: PropTypes.number,
isGrabbing: PropTypes.bool.isRequired,
isRemoving: PropTypes.bool.isRequired,
isRefreshMonitoredDownloadsExecuting: PropTypes.bool.isRequired,
onRefreshPress: PropTypes.func.isRequired,
onGrabSelectedPress: PropTypes.func.isRequired,
- onRemoveSelectedPress: PropTypes.func.isRequired
+ onRemoveSelectedPress: PropTypes.func.isRequired,
+ onFilterSelect: PropTypes.func.isRequired
};
export default Queue;
diff --git a/frontend/src/Activity/Queue/QueueConnector.js b/frontend/src/Activity/Queue/QueueConnector.js
index f7d5d5152..178cb8e5f 100644
--- a/frontend/src/Activity/Queue/QueueConnector.js
+++ b/frontend/src/Activity/Queue/QueueConnector.js
@@ -7,6 +7,7 @@ import withCurrentPage from 'Components/withCurrentPage';
import { executeCommand } from 'Store/Actions/commandActions';
import { clearEpisodes, fetchEpisodes } from 'Store/Actions/episodeActions';
import * as queueActions from 'Store/Actions/queueActions';
+import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import selectUniqueIds from 'Utilities/Object/selectUniqueIds';
@@ -18,12 +19,16 @@ function createMapStateToProps() {
(state) => state.episodes,
(state) => state.queue.options,
(state) => state.queue.paged,
+ (state) => state.queue.status.item,
+ createCustomFiltersSelector('queue'),
createCommandExecutingSelector(commandNames.REFRESH_MONITORED_DOWNLOADS),
- (episodes, options, queue, isRefreshMonitoredDownloadsExecuting) => {
+ (episodes, options, queue, status, customFilters, isRefreshMonitoredDownloadsExecuting) => {
return {
+ count: options.includeUnknownSeriesItems ? status.totalCount : status.count,
isEpisodesFetching: episodes.isFetching,
isEpisodesPopulated: episodes.isPopulated,
episodesError: episodes.error,
+ customFilters,
isRefreshMonitoredDownloadsExecuting,
...options,
...queue
@@ -122,6 +127,10 @@ class QueueConnector extends Component {
this.props.setQueueSort({ sortKey });
};
+ onFilterSelect = (selectedFilterKey) => {
+ this.props.setQueueFilter({ selectedFilterKey });
+ };
+
onTableOptionChange = (payload) => {
this.props.setQueueTableOption(payload);
@@ -156,6 +165,7 @@ class QueueConnector extends Component {
onLastPagePress={this.onLastPagePress}
onPageSelect={this.onPageSelect}
onSortPress={this.onSortPress}
+ onFilterSelect={this.onFilterSelect}
onTableOptionChange={this.onTableOptionChange}
onRefreshPress={this.onRefreshPress}
onGrabSelectedPress={this.onGrabSelectedPress}
@@ -178,6 +188,7 @@ QueueConnector.propTypes = {
gotoQueueLastPage: PropTypes.func.isRequired,
gotoQueuePage: PropTypes.func.isRequired,
setQueueSort: PropTypes.func.isRequired,
+ setQueueFilter: PropTypes.func.isRequired,
setQueueTableOption: PropTypes.func.isRequired,
clearQueue: PropTypes.func.isRequired,
grabQueueItems: PropTypes.func.isRequired,
diff --git a/frontend/src/Activity/Queue/QueueFilterModal.tsx b/frontend/src/Activity/Queue/QueueFilterModal.tsx
new file mode 100644
index 000000000..3fce6c166
--- /dev/null
+++ b/frontend/src/Activity/Queue/QueueFilterModal.tsx
@@ -0,0 +1,54 @@
+import React, { useCallback } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { createSelector } from 'reselect';
+import AppState from 'App/State/AppState';
+import FilterModal from 'Components/Filter/FilterModal';
+import { setQueueFilter } from 'Store/Actions/queueActions';
+
+function createQueueSelector() {
+ return createSelector(
+ (state: AppState) => state.queue.paged.items,
+ (queueItems) => {
+ return queueItems;
+ }
+ );
+}
+
+function createFilterBuilderPropsSelector() {
+ return createSelector(
+ (state: AppState) => state.queue.paged.filterBuilderProps,
+ (filterBuilderProps) => {
+ return filterBuilderProps;
+ }
+ );
+}
+
+interface QueueFilterModalProps {
+ isOpen: boolean;
+}
+
+export default function QueueFilterModal(props: QueueFilterModalProps) {
+ const sectionItems = useSelector(createQueueSelector());
+ const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
+ const customFilterType = 'queue';
+
+ const dispatch = useDispatch();
+
+ const dispatchSetFilter = useCallback(
+ (payload: unknown) => {
+ dispatch(setQueueFilter(payload));
+ },
+ [dispatch]
+ );
+
+ return (
+
+ );
+}
diff --git a/frontend/src/App/State/AppSectionState.ts b/frontend/src/App/State/AppSectionState.ts
index d511963fc..cabc39b1c 100644
--- a/frontend/src/App/State/AppSectionState.ts
+++ b/frontend/src/App/State/AppSectionState.ts
@@ -1,4 +1,5 @@
import SortDirection from 'Helpers/Props/SortDirection';
+import { FilterBuilderProp } from './AppState';
export interface Error {
responseJSON: {
@@ -20,6 +21,10 @@ export interface PagedAppSectionState {
pageSize: number;
}
+export interface AppSectionFilterState {
+ filterBuilderProps: FilterBuilderProp[];
+}
+
export interface AppSectionSchemaState {
isSchemaFetching: boolean;
isSchemaPopulated: boolean;
diff --git a/frontend/src/App/State/QueueAppState.ts b/frontend/src/App/State/QueueAppState.ts
index 05fc5a59a..a2936109d 100644
--- a/frontend/src/App/State/QueueAppState.ts
+++ b/frontend/src/App/State/QueueAppState.ts
@@ -2,7 +2,11 @@ import ModelBase from 'App/ModelBase';
import Language from 'Language/Language';
import { QualityModel } from 'Quality/Quality';
import CustomFormat from 'typings/CustomFormat';
-import AppSectionState, { AppSectionItemState, Error } from './AppSectionState';
+import AppSectionState, {
+ AppSectionFilterState,
+ AppSectionItemState,
+ Error,
+} from './AppSectionState';
export interface StatusMessage {
title: string;
@@ -37,7 +41,9 @@ export interface QueueDetailsAppState extends AppSectionState {
params: unknown;
}
-export interface QueuePagedAppState extends AppSectionState {
+export interface QueuePagedAppState
+ extends AppSectionState,
+ AppSectionFilterState {
isGrabbing: boolean;
grabError: Error;
isRemoving: boolean;
diff --git a/frontend/src/Components/Filter/Builder/FilterBuilderRow.js b/frontend/src/Components/Filter/Builder/FilterBuilderRow.js
index ee92b395d..6591bc4b7 100644
--- a/frontend/src/Components/Filter/Builder/FilterBuilderRow.js
+++ b/frontend/src/Components/Filter/Builder/FilterBuilderRow.js
@@ -7,9 +7,11 @@ import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
+import LanguageFilterBuilderRowValue from './LanguageFilterBuilderRowValue';
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
import QualityFilterBuilderRowValueConnector from './QualityFilterBuilderRowValueConnector';
import QualityProfileFilterBuilderRowValueConnector from './QualityProfileFilterBuilderRowValueConnector';
+import SeriesFilterBuilderRowValue from './SeriesFilterBuilderRowValue';
import SeriesStatusFilterBuilderRowValue from './SeriesStatusFilterBuilderRowValue';
import SeriesTypeFilterBuilderRowValue from './SeriesTypeFilterBuilderRowValue';
import TagFilterBuilderRowValueConnector from './TagFilterBuilderRowValueConnector';
@@ -60,6 +62,9 @@ function getRowValueConnector(selectedFilterBuilderProp) {
case filterBuilderValueTypes.INDEXER:
return IndexerFilterBuilderRowValueConnector;
+ case filterBuilderValueTypes.LANGUAGE:
+ return LanguageFilterBuilderRowValue;
+
case filterBuilderValueTypes.PROTOCOL:
return ProtocolFilterBuilderRowValue;
@@ -69,6 +74,9 @@ function getRowValueConnector(selectedFilterBuilderProp) {
case filterBuilderValueTypes.QUALITY_PROFILE:
return QualityProfileFilterBuilderRowValueConnector;
+ case filterBuilderValueTypes.SERIES:
+ return SeriesFilterBuilderRowValue;
+
case filterBuilderValueTypes.SERIES_STATUS:
return SeriesStatusFilterBuilderRowValue;
diff --git a/frontend/src/Components/Filter/Builder/FilterBuilderRowValueProps.ts b/frontend/src/Components/Filter/Builder/FilterBuilderRowValueProps.ts
new file mode 100644
index 000000000..5bf9e5785
--- /dev/null
+++ b/frontend/src/Components/Filter/Builder/FilterBuilderRowValueProps.ts
@@ -0,0 +1,16 @@
+import { FilterBuilderProp } from 'App/State/AppState';
+
+interface FilterBuilderRowOnChangeProps {
+ name: string;
+ value: unknown[];
+}
+
+interface FilterBuilderRowValueProps {
+ filterType?: string;
+ filterValue: string | number | object | string[] | number[] | object[];
+ selectedFilterBuilderProp: FilterBuilderProp;
+ sectionItem: unknown[];
+ onChange: (payload: FilterBuilderRowOnChangeProps) => void;
+}
+
+export default FilterBuilderRowValueProps;
diff --git a/frontend/src/Components/Filter/Builder/LanguageFilterBuilderRowValue.tsx b/frontend/src/Components/Filter/Builder/LanguageFilterBuilderRowValue.tsx
new file mode 100644
index 000000000..e828fd848
--- /dev/null
+++ b/frontend/src/Components/Filter/Builder/LanguageFilterBuilderRowValue.tsx
@@ -0,0 +1,13 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import createLanguagesSelector from 'Store/Selectors/createLanguagesSelector';
+import FilterBuilderRowValue from './FilterBuilderRowValue';
+import FilterBuilderRowValueProps from './FilterBuilderRowValueProps';
+
+function LanguageFilterBuilderRowValue(props: FilterBuilderRowValueProps) {
+ const { items } = useSelector(createLanguagesSelector());
+
+ return ;
+}
+
+export default LanguageFilterBuilderRowValue;
diff --git a/frontend/src/Components/Filter/Builder/SeriesFilterBuilderRowValue.tsx b/frontend/src/Components/Filter/Builder/SeriesFilterBuilderRowValue.tsx
new file mode 100644
index 000000000..418874fc7
--- /dev/null
+++ b/frontend/src/Components/Filter/Builder/SeriesFilterBuilderRowValue.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import Series from 'Series/Series';
+import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
+import FilterBuilderRowValue from './FilterBuilderRowValue';
+import FilterBuilderRowValueProps from './FilterBuilderRowValueProps';
+
+function SeriesFilterBuilderRowValue(props: FilterBuilderRowValueProps) {
+ const allSeries: Series[] = useSelector(createAllSeriesSelector());
+
+ const tagList = allSeries.map((series) => {
+ return {
+ id: series.id,
+ name: series.title,
+ };
+ });
+
+ return ;
+}
+
+export default SeriesFilterBuilderRowValue;
diff --git a/frontend/src/Helpers/Props/filterBuilderValueTypes.js b/frontend/src/Helpers/Props/filterBuilderValueTypes.js
index a1f8f499d..49b6fcbb6 100644
--- a/frontend/src/Helpers/Props/filterBuilderValueTypes.js
+++ b/frontend/src/Helpers/Props/filterBuilderValueTypes.js
@@ -3,9 +3,11 @@ export const BYTES = 'bytes';
export const DATE = 'date';
export const DEFAULT = 'default';
export const INDEXER = 'indexer';
+export const LANGUAGE = 'language';
export const PROTOCOL = 'protocol';
export const QUALITY = 'quality';
export const QUALITY_PROFILE = 'qualityProfile';
+export const SERIES = 'series';
export const SERIES_STATUS = 'seriesStatus';
export const SERIES_TYPES = 'seriesType';
export const TAG = 'tag';
diff --git a/frontend/src/Store/Actions/Creators/createFetchServerSideCollectionHandler.js b/frontend/src/Store/Actions/Creators/createFetchServerSideCollectionHandler.js
index a80ee1e45..f5ef10a4d 100644
--- a/frontend/src/Store/Actions/Creators/createFetchServerSideCollectionHandler.js
+++ b/frontend/src/Store/Actions/Creators/createFetchServerSideCollectionHandler.js
@@ -6,6 +6,8 @@ import getSectionState from 'Utilities/State/getSectionState';
import { set, updateServerSideCollection } from '../baseActions';
function createFetchServerSideCollectionHandler(section, url, fetchDataAugmenter) {
+ const [baseSection] = section.split('.');
+
return function(getState, payload, dispatch) {
dispatch(set({ section, isFetching: true }));
@@ -25,10 +27,13 @@ function createFetchServerSideCollectionHandler(section, url, fetchDataAugmenter
const {
selectedFilterKey,
- filters,
- customFilters
+ filters
} = sectionState;
+ const customFilters = getState().customFilters.items.filter((customFilter) => {
+ return customFilter.type === section || customFilter.type === baseSection;
+ });
+
const selectedFilters = findSelectedFilters(selectedFilterKey, filters, customFilters);
selectedFilters.forEach((filter) => {
@@ -37,7 +42,8 @@ function createFetchServerSideCollectionHandler(section, url, fetchDataAugmenter
const promise = createAjaxRequest({
url,
- data
+ data,
+ traditional: true
}).request;
promise.done((response) => {
diff --git a/frontend/src/Store/Actions/calendarActions.js b/frontend/src/Store/Actions/calendarActions.js
index 0e0febea6..4fece9523 100644
--- a/frontend/src/Store/Actions/calendarActions.js
+++ b/frontend/src/Store/Actions/calendarActions.js
@@ -52,8 +52,6 @@ export const defaultState = {
selectedFilterKey: 'monitored',
- customFilters: [],
-
filters: [
{
key: 'all',
diff --git a/frontend/src/Store/Actions/queueActions.js b/frontend/src/Store/Actions/queueActions.js
index 6c4ee38cc..15ea35561 100644
--- a/frontend/src/Store/Actions/queueActions.js
+++ b/frontend/src/Store/Actions/queueActions.js
@@ -3,7 +3,7 @@ import React from 'react';
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import Icon from 'Components/Icon';
-import { icons, sortDirections } from 'Helpers/Props';
+import { filterBuilderTypes, filterBuilderValueTypes, icons, sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import serverSideCollectionHandlers from 'Utilities/serverSideCollectionHandlers';
@@ -170,6 +170,43 @@ export const defaultState = {
isVisible: true,
isModifiable: false
}
+ ],
+
+ selectedFilterKey: 'all',
+
+ filters: [
+ {
+ key: 'all',
+ label: 'All',
+ filters: []
+ }
+ ],
+
+ filterBuilderProps: [
+ {
+ name: 'seriesIds',
+ label: 'Series',
+ type: filterBuilderTypes.EQUAL,
+ valueType: filterBuilderValueTypes.SERIES
+ },
+ {
+ name: 'quality',
+ label: 'Quality',
+ type: filterBuilderTypes.EQUAL,
+ valueType: filterBuilderValueTypes.QUALITY
+ },
+ {
+ name: 'languages',
+ label: 'Languages',
+ type: filterBuilderTypes.CONTAINS,
+ valueType: filterBuilderValueTypes.LANGUAGE
+ },
+ {
+ name: 'protocol',
+ label: 'Protocol',
+ type: filterBuilderTypes.EQUAL,
+ valueType: filterBuilderValueTypes.PROTOCOL
+ }
]
}
};
@@ -179,7 +216,8 @@ export const persistState = [
'queue.paged.pageSize',
'queue.paged.sortKey',
'queue.paged.sortDirection',
- 'queue.paged.columns'
+ 'queue.paged.columns',
+ 'queue.paged.selectedFilterKey'
];
//
@@ -204,6 +242,7 @@ export const GOTO_NEXT_QUEUE_PAGE = 'queue/gotoQueueNextPage';
export const GOTO_LAST_QUEUE_PAGE = 'queue/gotoQueueLastPage';
export const GOTO_QUEUE_PAGE = 'queue/gotoQueuePage';
export const SET_QUEUE_SORT = 'queue/setQueueSort';
+export const SET_QUEUE_FILTER = 'queue/setQueueFilter';
export const SET_QUEUE_TABLE_OPTION = 'queue/setQueueTableOption';
export const SET_QUEUE_OPTION = 'queue/setQueueOption';
export const CLEAR_QUEUE = 'queue/clearQueue';
@@ -228,6 +267,7 @@ export const gotoQueueNextPage = createThunk(GOTO_NEXT_QUEUE_PAGE);
export const gotoQueueLastPage = createThunk(GOTO_LAST_QUEUE_PAGE);
export const gotoQueuePage = createThunk(GOTO_QUEUE_PAGE);
export const setQueueSort = createThunk(SET_QUEUE_SORT);
+export const setQueueFilter = createThunk(SET_QUEUE_FILTER);
export const setQueueTableOption = createAction(SET_QUEUE_TABLE_OPTION);
export const setQueueOption = createAction(SET_QUEUE_OPTION);
export const clearQueue = createAction(CLEAR_QUEUE);
@@ -279,7 +319,8 @@ export const actionHandlers = handleThunks({
[serverSideCollectionHandlers.NEXT_PAGE]: GOTO_NEXT_QUEUE_PAGE,
[serverSideCollectionHandlers.LAST_PAGE]: GOTO_LAST_QUEUE_PAGE,
[serverSideCollectionHandlers.EXACT_PAGE]: GOTO_QUEUE_PAGE,
- [serverSideCollectionHandlers.SORT]: SET_QUEUE_SORT
+ [serverSideCollectionHandlers.SORT]: SET_QUEUE_SORT,
+ [serverSideCollectionHandlers.FILTER]: SET_QUEUE_FILTER
},
fetchDataAugmenter
),
diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json
index f772d5047..47e36054a 100644
--- a/src/NzbDrone.Core/Localization/Core/en.json
+++ b/src/NzbDrone.Core/Localization/Core/en.json
@@ -1008,6 +1008,7 @@
"QualitySettings": "Quality Settings",
"QualitySettingsSummary": "Quality sizes and naming",
"Queue": "Queue",
+ "QueueFilterHasNoItems": "Selected queue filter has no items",
"QueueIsEmpty": "Queue is empty",
"QueueLoadError": "Failed to load Queue",
"Queued": "Queued",
diff --git a/src/Sonarr.Api.V3/Queue/QueueController.cs b/src/Sonarr.Api.V3/Queue/QueueController.cs
index edb631ee4..c3e4c1d52 100644
--- a/src/Sonarr.Api.V3/Queue/QueueController.cs
+++ b/src/Sonarr.Api.V3/Queue/QueueController.cs
@@ -9,6 +9,7 @@ using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Download.TrackedDownloads;
+using NzbDrone.Core.Indexers;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Profiles.Qualities;
@@ -135,15 +136,15 @@ namespace Sonarr.Api.V3.Queue
[HttpGet]
[Produces("application/json")]
- public PagingResource GetQueue([FromQuery] PagingRequestResource paging, bool includeUnknownSeriesItems = false, bool includeSeries = false, bool includeEpisode = false)
+ public PagingResource GetQueue([FromQuery] PagingRequestResource paging, bool includeUnknownSeriesItems = false, bool includeSeries = false, bool includeEpisode = false, [FromQuery] int[] seriesIds = null, DownloadProtocol? protocol = null, [FromQuery] int[] languages = null, int? quality = null)
{
var pagingResource = new PagingResource(paging);
var pagingSpec = pagingResource.MapToPagingSpec("timeleft", SortDirection.Ascending);
- return pagingSpec.ApplyToPage((spec) => GetQueue(spec, includeUnknownSeriesItems), (q) => MapToResource(q, includeSeries, includeEpisode));
+ return pagingSpec.ApplyToPage((spec) => GetQueue(spec, seriesIds?.ToHashSet(), protocol, languages?.ToHashSet(), quality, includeUnknownSeriesItems), (q) => MapToResource(q, includeSeries, includeEpisode));
}
- private PagingSpec GetQueue(PagingSpec pagingSpec, bool includeUnknownSeriesItems)
+ private PagingSpec GetQueue(PagingSpec pagingSpec, HashSet seriesIds, DownloadProtocol? protocol, HashSet languages, int? quality, bool includeUnknownSeriesItems)
{
var ascending = pagingSpec.SortDirection == SortDirection.Ascending;
var orderByFunc = GetOrderByFunc(pagingSpec);
@@ -151,7 +152,36 @@ namespace Sonarr.Api.V3.Queue
var queue = _queueService.GetQueue();
var filteredQueue = includeUnknownSeriesItems ? queue : queue.Where(q => q.Series != null);
var pending = _pendingReleaseService.GetPendingQueue();
- var fullQueue = filteredQueue.Concat(pending).ToList();
+
+ var hasSeriesIdFilter = seriesIds.Any();
+ var hasLanguageFilter = languages.Any();
+ var fullQueue = filteredQueue.Concat(pending).Where(q =>
+ {
+ var include = true;
+
+ if (hasSeriesIdFilter)
+ {
+ include &= q.Series != null && seriesIds.Contains(q.Series.Id);
+ }
+
+ if (include && protocol.HasValue)
+ {
+ include &= q.Protocol == protocol.Value;
+ }
+
+ if (include && hasLanguageFilter)
+ {
+ include &= q.Languages.Any(l => languages.Contains(l.Id));
+ }
+
+ if (include && quality.HasValue)
+ {
+ include &= q.Quality.Quality.Id == quality.Value;
+ }
+
+ return include;
+ }).ToList();
+
IOrderedEnumerable ordered;
if (pagingSpec.SortKey == "timeleft")