Skip to content

Commit 8f492d7

Browse files
authored
Merge branch 'master' into fix/incorrect-redirect-after-delete-v2
2 parents 34bb8c1 + 7759788 commit 8f492d7

15 files changed

Lines changed: 503 additions & 28 deletions

File tree

src/apps/modern/features/libraries/components/filter/FilterButton.tsx

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,18 @@ import Badge from '@mui/material/Badge';
1515
import { styled } from '@mui/material/styles';
1616
import Typography from '@mui/material/Typography';
1717

18-
import { useGetQueryFiltersLegacy, useGetStudios } from 'hooks/useFetchItems';
18+
import { useGetQueryFilters, useGetQueryFiltersLegacy, useGetStudios } from 'hooks/useFetchItems';
1919
import globalize from 'lib/globalize';
2020

21+
import FiltersAudioLanguages from './FiltersAudioLanguages';
2122
import FiltersFeatures from './FiltersFeatures';
2223
import FiltersGenres from './FiltersGenres';
2324
import FiltersOfficialRatings from './FiltersOfficialRatings';
2425
import FiltersEpisodesStatus from './FiltersEpisodesStatus';
2526
import FiltersSeriesStatus from './FiltersSeriesStatus';
2627
import FiltersStatus from './FiltersStatus';
2728
import FiltersStudios from './FiltersStudios';
29+
import FiltersSubtitleLanguages from './FiltersSubtitleLanguages';
2830
import FiltersTags from './FiltersTags';
2931
import FiltersVideoTypes from './FiltersVideoTypes';
3032
import FiltersYears from './FiltersYears';
@@ -99,7 +101,8 @@ const FilterButton: FC<FilterButtonProps> = ({
99101
const open = Boolean(anchorEl);
100102
const id = open ? 'filter-popover' : undefined;
101103

102-
const { data } = useGetQueryFiltersLegacy(parentId, itemType);
104+
const { data: filtersLegacy } = useGetQueryFiltersLegacy(parentId, itemType);
105+
const { data: filters } = useGetQueryFilters(parentId, itemType);
103106
const { data: studios } = useGetStudios(parentId, itemType);
104107

105108
const handleChange =
@@ -173,6 +176,13 @@ const FilterButton: FC<FilterButtonProps> = ({
173176
return viewType === LibraryTab.Episodes;
174177
};
175178

179+
const isFiltersLanguagesEnabled = () => {
180+
return viewType === LibraryTab.Movies
181+
|| viewType === LibraryTab.Series
182+
|| viewType === LibraryTab.Episodes
183+
|| viewType === LibraryTab.Mixed;
184+
};
185+
176186
return (
177187
<>
178188
<Button
@@ -324,7 +334,7 @@ const FilterButton: FC<FilterButtonProps> = ({
324334

325335
{isFiltersLegacyEnabled() && (
326336
<>
327-
{data?.Genres && data?.Genres?.length > 0 && (
337+
{filtersLegacy?.Genres && filtersLegacy?.Genres?.length > 0 && (
328338
<Accordion
329339
expanded={expanded === 'filtersGenres'}
330340
onChange={handleChange('filtersGenres')}
@@ -339,7 +349,7 @@ const FilterButton: FC<FilterButtonProps> = ({
339349
</AccordionSummary>
340350
<AccordionDetails>
341351
<FiltersGenres
342-
genresOptions={data.Genres}
352+
genresOptions={filtersLegacy.Genres}
343353
libraryViewSettings={
344354
libraryViewSettings
345355
}
@@ -351,8 +361,8 @@ const FilterButton: FC<FilterButtonProps> = ({
351361
</Accordion>
352362
)}
353363

354-
{data?.OfficialRatings
355-
&& data?.OfficialRatings?.length > 0 && (
364+
{filtersLegacy?.OfficialRatings
365+
&& filtersLegacy?.OfficialRatings?.length > 0 && (
356366
<Accordion
357367
expanded={
358368
expanded === 'filtersOfficialRatings'
@@ -373,7 +383,7 @@ const FilterButton: FC<FilterButtonProps> = ({
373383
</AccordionSummary>
374384
<AccordionDetails>
375385
<FiltersOfficialRatings
376-
OfficialRatingsOptions={data.OfficialRatings}
386+
OfficialRatingsOptions={filtersLegacy.OfficialRatings}
377387
libraryViewSettings={
378388
libraryViewSettings
379389
}
@@ -385,7 +395,7 @@ const FilterButton: FC<FilterButtonProps> = ({
385395
</Accordion>
386396
)}
387397

388-
{data?.Tags && data?.Tags.length > 0 && (
398+
{filtersLegacy?.Tags && filtersLegacy?.Tags.length > 0 && (
389399
<Accordion
390400
expanded={expanded === 'filtersTags'}
391401
onChange={handleChange('filtersTags')}
@@ -400,7 +410,7 @@ const FilterButton: FC<FilterButtonProps> = ({
400410
</AccordionSummary>
401411
<AccordionDetails>
402412
<FiltersTags
403-
tagsOptions={data.Tags}
413+
tagsOptions={filtersLegacy.Tags}
404414
libraryViewSettings={
405415
libraryViewSettings
406416
}
@@ -412,7 +422,7 @@ const FilterButton: FC<FilterButtonProps> = ({
412422
</Accordion>
413423
)}
414424

415-
{data?.Years && data?.Years?.length > 0 && (
425+
{filtersLegacy?.Years && filtersLegacy?.Years?.length > 0 && (
416426
<Accordion
417427
expanded={expanded === 'filtersYears'}
418428
onChange={handleChange('filtersYears')}
@@ -427,7 +437,7 @@ const FilterButton: FC<FilterButtonProps> = ({
427437
</AccordionSummary>
428438
<AccordionDetails>
429439
<FiltersYears
430-
yearsOptions={data.Years}
440+
yearsOptions={filtersLegacy.Years}
431441
libraryViewSettings={
432442
libraryViewSettings
433443
}
@@ -464,6 +474,50 @@ const FilterButton: FC<FilterButtonProps> = ({
464474
</AccordionDetails>
465475
</Accordion>
466476
)}
477+
{isFiltersLanguagesEnabled() && !!filters?.AudioLanguages?.length && (
478+
<Accordion
479+
expanded={expanded === 'filtersAudioLanguages'}
480+
onChange={handleChange('filtersAudioLanguages')}
481+
>
482+
<AccordionSummary
483+
aria-controls='audioLanguages-content'
484+
id='filtersAudioLanguages-header'
485+
>
486+
<Typography>
487+
{globalize.translate('AudioLanguages')}
488+
</Typography>
489+
</AccordionSummary>
490+
<AccordionDetails>
491+
<FiltersAudioLanguages
492+
options={filters.AudioLanguages}
493+
libraryViewSettings={libraryViewSettings}
494+
setLibraryViewSettings={setLibraryViewSettings}
495+
/>
496+
</AccordionDetails>
497+
</Accordion>
498+
)}
499+
{isFiltersLanguagesEnabled() && !!filters?.SubtitleLanguages?.length && (
500+
<Accordion
501+
expanded={expanded === 'filtersSubtitleLanguages'}
502+
onChange={handleChange('filtersSubtitleLanguages')}
503+
>
504+
<AccordionSummary
505+
aria-controls='subtitleLanguages-content'
506+
id='filtersSubtitleLanguages-header'
507+
>
508+
<Typography>
509+
{globalize.translate('SubtitleLanguages')}
510+
</Typography>
511+
</AccordionSummary>
512+
<AccordionDetails>
513+
<FiltersSubtitleLanguages
514+
options={filters.SubtitleLanguages}
515+
libraryViewSettings={libraryViewSettings}
516+
setLibraryViewSettings={setLibraryViewSettings}
517+
/>
518+
</AccordionDetails>
519+
</Accordion>
520+
)}
467521
<Button
468522
disabled={!hasFilters}
469523
title={globalize.translate('ResetFilters')}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import Checkbox from '@mui/material/Checkbox';
2+
import FormControlLabel from '@mui/material/FormControlLabel';
3+
import FormGroup from '@mui/material/FormGroup';
4+
import React, { FC, useCallback } from 'react';
5+
import { LibraryViewSettings } from 'types/library';
6+
7+
interface FiltersAudioLanguagesProps {
8+
options: { Name?: string | null, Value?: string | null }[];
9+
libraryViewSettings: LibraryViewSettings;
10+
setLibraryViewSettings: React.Dispatch<React.SetStateAction<LibraryViewSettings>>;
11+
}
12+
13+
const FiltersAudioLanguages: FC<FiltersAudioLanguagesProps> = ({
14+
options,
15+
libraryViewSettings,
16+
setLibraryViewSettings
17+
}) => {
18+
const onFiltersChange = useCallback(
19+
(event: React.ChangeEvent<HTMLInputElement>) => {
20+
event.preventDefault();
21+
const value = event.target.value;
22+
const existingValues = libraryViewSettings?.Filters?.AudioLanguages ?? [];
23+
24+
const updatedValues = existingValues.includes(value) ?
25+
existingValues.filter((filter) => filter !== value) :
26+
[...existingValues, value];
27+
28+
setLibraryViewSettings((prevState) => ({
29+
...prevState,
30+
StartIndex: 0,
31+
Filters: {
32+
...prevState.Filters,
33+
AudioLanguages: updatedValues.length ? updatedValues : undefined
34+
}
35+
}));
36+
},
37+
[setLibraryViewSettings, libraryViewSettings?.Filters?.AudioLanguages]
38+
);
39+
40+
return (
41+
<FormGroup>
42+
{options.filter((filter) => filter?.Value != null).map((filter) => (
43+
<FormControlLabel
44+
key={filter.Value}
45+
control={
46+
<Checkbox
47+
checked={!!libraryViewSettings?.Filters?.AudioLanguages?.includes(filter.Value!)}
48+
onChange={onFiltersChange}
49+
value={filter.Value}
50+
/>
51+
}
52+
label={filter.Name}
53+
/>
54+
))}
55+
</FormGroup>
56+
);
57+
};
58+
59+
export default FiltersAudioLanguages;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import Checkbox from '@mui/material/Checkbox';
2+
import FormControlLabel from '@mui/material/FormControlLabel';
3+
import FormGroup from '@mui/material/FormGroup';
4+
import React, { FC, useCallback } from 'react';
5+
import { LibraryViewSettings } from 'types/library';
6+
7+
interface FiltersSubtitleLanguagesProps {
8+
options: { Name?: string | null, Value?: string | null }[];
9+
libraryViewSettings: LibraryViewSettings;
10+
setLibraryViewSettings: React.Dispatch<React.SetStateAction<LibraryViewSettings>>;
11+
}
12+
13+
const FiltersSubtitleLanguages: FC<FiltersSubtitleLanguagesProps> = ({
14+
options,
15+
libraryViewSettings,
16+
setLibraryViewSettings
17+
}) => {
18+
const onFiltersChange = useCallback(
19+
(event: React.ChangeEvent<HTMLInputElement>) => {
20+
event.preventDefault();
21+
const value = event.target.value;
22+
const existingValues = libraryViewSettings?.Filters?.SubtitleLanguages ?? [];
23+
24+
const updatedValues = existingValues.includes(value) ?
25+
existingValues.filter((filter) => filter !== value) :
26+
[...existingValues, value];
27+
28+
setLibraryViewSettings((prevState) => ({
29+
...prevState,
30+
StartIndex: 0,
31+
Filters: {
32+
...prevState.Filters,
33+
SubtitleLanguages: updatedValues.length ? updatedValues : undefined
34+
}
35+
}));
36+
},
37+
[setLibraryViewSettings, libraryViewSettings?.Filters?.SubtitleLanguages]
38+
);
39+
40+
return (
41+
<FormGroup>
42+
{options.filter((filter) => filter?.Value != null).map((filter) => (
43+
<FormControlLabel
44+
key={filter.Value}
45+
control={
46+
<Checkbox
47+
checked={!!libraryViewSettings?.Filters?.SubtitleLanguages?.includes(filter.Value!)}
48+
onChange={onFiltersChange}
49+
value={filter.Value}
50+
/>
51+
}
52+
label={filter.Name}
53+
/>
54+
))}
55+
</FormGroup>
56+
);
57+
};
58+
59+
export default FiltersSubtitleLanguages;

src/hooks/useFetchItems.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,28 @@ const fetchGetQueryFiltersLegacy = async (
169169
}
170170
};
171171

172+
const fetchGetQueryFilters = async (
173+
currentApi: JellyfinApiContext,
174+
parentId: ParentId,
175+
itemType: BaseItemKind[],
176+
options?: AxiosRequestConfig
177+
) => {
178+
const { api, user } = currentApi;
179+
if (api && user?.Id) {
180+
const response = await getFilterApi(api).getQueryFilters(
181+
{
182+
userId: user.Id,
183+
parentId: parentId ?? undefined,
184+
includeItemTypes: itemType
185+
},
186+
{
187+
signal: options?.signal
188+
}
189+
);
190+
return response.data;
191+
}
192+
};
193+
172194
export const useGetQueryFiltersLegacy = (
173195
parentId: ParentId,
174196
itemType: BaseItemKind[]
@@ -185,6 +207,22 @@ export const useGetQueryFiltersLegacy = (
185207
});
186208
};
187209

210+
export const useGetQueryFilters = (
211+
parentId: ParentId,
212+
itemType: BaseItemKind[]
213+
) => {
214+
const currentApi = useApi();
215+
const isLivetv = parentId === 'livetv';
216+
return useQuery({
217+
queryKey: ['QueryFilters', parentId, itemType],
218+
queryFn: ({ signal }) =>
219+
fetchGetQueryFilters(currentApi, parentId, itemType, {
220+
signal
221+
}),
222+
enabled: !!currentApi.api && !!currentApi.user?.Id && !!parentId && !isLivetv
223+
});
224+
};
225+
188226
const fetchGetItemsViewByType = async (
189227
currentApi: JellyfinApiContext,
190228
viewType: LibraryTab | undefined,

src/plugins/syncPlay/core/Manager.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class Manager {
6161

6262
Events.on(this.timeSyncCore, 'time-sync-server-update', (event, timeOffset, ping) => {
6363
// Report ping back to server.
64-
if (this.syncEnabled) {
64+
if (this.isSyncPlayEnabled()) {
6565
this.getApiClient().sendSyncPlayPing({
6666
Ping: ping
6767
});

src/plugins/syncPlay/core/timeSync/TimeSync.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class Measurement {
4949
* Get ping time, in milliseconds.
5050
*/
5151
getPing() {
52-
return this.getDelay() / 2;
52+
return Math.round(this.getDelay() / 2);
5353
}
5454
}
5555

src/strings/ca.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1924,5 +1924,6 @@
19241924
"StillWatchingPrompt": "Avís de continuació de reproducció",
19251925
"StopWatching": "Deixa de veure",
19261926
"NoCastTargets": "No hi ha objectius d'emissió disponibles",
1927-
"IndexNumber": "Número d'índex"
1927+
"IndexNumber": "Número d'índex",
1928+
"MessageNoEpisodesFound": "Actualment, no hi ha cap episodi disponible."
19281929
}

src/strings/en-us.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1931,5 +1931,7 @@
19311931
"LabelProgressAndSpeed": "Progress / speed",
19321932
"LabelReasons": "Reasons",
19331933
"LabelPlayerSizes": "Player sizes",
1934-
"LabelPlaybackQuality": "Frame dropped / corrupted"
1934+
"LabelPlaybackQuality": "Frame dropped / corrupted",
1935+
"AudioLanguages": "Audio languages",
1936+
"SubtitleLanguages": "Subtitle languages"
19351937
}

0 commit comments

Comments
 (0)