Compare commits

...

21 Commits

Author SHA1 Message Date
Fallenbagel
0259975402 fix: fixes jellyfin forgot password and adds emby support to the forgot password link
Fixes jellyfin forgot password as it had a wrong url that lead to an empty page and also adds in
emby forgot password link if the environmental variable is set

fix #99
2022-05-26 10:11:53 +05:00
Fallenbagel
71d33e47a9 Merge pull request #109 from sambartik/revert-104-fix-profilepic
Revert "Fix non-square profile pictures being squished"
2022-05-26 00:48:49 +05:00
notfakie
93a9cff2ef Merge remote-tracking branch 'overseerr/develop' into up-develop 2022-05-25 18:34:49 +12:00
Samuel Bartík
e9fa94097a Revert "Fix non-square profile pictures being squished" 2022-05-24 19:50:37 +02:00
Fallenbagel
046ae932ec Merge pull request #104 from sambartik/fix-profilepic
Fix non-square profile pictures being squished
2022-05-24 07:02:17 +05:00
Samuel Bartík
1570a8715a Fix non-square profile pictures being squished 2022-05-23 15:36:00 +02:00
Danshil Kokil Mungur
90095bb185 feat(manage slideover): show more request override details (#2772)
* feat(manage slideover): show the language profile if request is for a show

* feat(manage slideover): show name of profiles instead of id
2022-05-23 09:49:35 +09:00
Brandon Cohen
4f972be858 fix(recommendations): fixed recommendations page causing infinite network requests to tmdb api
TMDB API would only return 40 results and the recommendations page expected more. This would cause
an infinite amount of network requests. A limit was implemented to specifically to solve this
problem
2022-05-21 07:35:02 +05:00
Danshil Kokil Mungur
b07f7032ad fix(search): use correct param to filter movies by year 2022-05-21 07:31:59 +05:00
Danshil Kokil Mungur
af23a257d5 feat(api): add issue counts endpoint 2022-05-21 07:29:16 +05:00
Danshil Kokil Mungur
dec4062cdc fix: don't show 0 playcount in slideover 2022-05-21 07:25:33 +05:00
Gian Marco Cinalli
e42153c599 docs: update fail2ban documentation to handle logging changes 2022-05-21 07:24:32 +05:00
TheCatLady
d22bc09652 feat: add Paramount+ to network slider 2022-05-21 07:22:32 +05:00
Brandon Cohen
9ded45fef8 fix: manual browser refresh would redirect to home on search page
If you used the search feature and tried to manual refresh or share the link, it would reset the
query. Taking you back to the home page.
2022-05-21 07:17:02 +05:00
Brandon Cohen
14519ef555 fix(recommendations): only load more titles if there can be more than 40 (#2749)
* fix: fixed recommendations page causing infinite network requests to tmdb api

TMDB API would only return 40 results and the recommendations page expected more. This would cause
an infinite amount of network requests. I set a limit specifically for this solving the problem.

fix #2710
2022-05-13 23:15:53 +04:00
Danshil Kokil Mungur
1054b4e2d7 fix(search): use correct param to filter movies by year (#2727) 2022-04-30 23:31:02 +09:00
Danshil Kokil Mungur
e4039d09c0 feat(api): add issue counts endpoint (#2713) 2022-04-25 23:06:36 +00:00
Danshil Kokil Mungur
29be659512 fix(ui): don't show 0 playcount in slideover (#2714) 2022-04-26 07:53:33 +09:00
Gian Marco Cinalli
475314c87b docs: update fail2ban documentation to handle logging changes (#2707) [skip ci] 2022-04-25 16:13:41 +00:00
TheCatLady
1d00229a48 feat(discover): add Paramount+ to network slider (#2608) 2022-04-25 13:09:22 +00:00
Brandon Cohen
b2878390b4 fix: manual browser refresh would redirect to home on search page (#2692)
If you used the search feature and tried to manual refresh or share the link, it would reset the
query. Taking you back to the home page.

fix #2683
2022-04-25 14:47:38 +04:00
12 changed files with 169 additions and 33 deletions

View File

@@ -8,7 +8,7 @@ To use Fail2ban with Overseerr, create a new file named `overseerr.local` in you
```
[Definition]
failregex = .*\[info\]\[Auth\]\: Failed sign-in attempt.*"ip":"<HOST>"
failregex = .*\[warn\]\[API\]\: Failed sign-in attempt.*"ip":"<HOST>"
```
You can then add a jail using this filter in `jail.local`. Please see the [Fail2ban documetation](https://www.fail2ban.org/wiki/index.php/MANUAL_0_8#Jails) for details on how to configure the jail.

View File

@@ -5815,6 +5815,36 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Issue'
/issue/count:
get:
summary: Gets issue counts
description: |
Returns the number of open and closed issues, as well as the number of issues of each type.
tags:
- issue
responses:
'200':
description: Issue counts returned
content:
application/json:
schema:
type: object
properties:
total:
type: number
video:
type: number
audio:
type: number
subtitles:
type: number
others:
type: number
open:
type: number
closed:
type: number
/issue/{issueId}:
get:
summary: Get issue

View File

@@ -129,7 +129,13 @@ class TheMovieDb extends ExternalAPI {
}: SingleSearchOptions): Promise<TmdbSearchMovieResponse> => {
try {
const data = await this.get<TmdbSearchMovieResponse>('/search/movie', {
params: { query, page, include_adult: includeAdult, language, year },
params: {
query,
page,
include_adult: includeAdult,
language,
primary_release_year: year,
},
});
return data;

View File

@@ -1,6 +1,6 @@
import { Router } from 'express';
import { getRepository } from 'typeorm';
import { IssueStatus } from '../constants/issue';
import { IssueStatus, IssueType } from '../constants/issue';
import Issue from '../entity/Issue';
import IssueComment from '../entity/IssueComment';
import Media from '../entity/Media';
@@ -146,6 +146,68 @@ issueRoutes.post<
}
);
issueRoutes.get('/count', async (req, res, next) => {
const issueRepository = getRepository(Issue);
try {
const query = issueRepository.createQueryBuilder('issue');
const totalCount = await query.getCount();
const videoCount = await query
.where('issue.issueType = :issueType', {
issueType: IssueType.VIDEO,
})
.getCount();
const audioCount = await query
.where('issue.issueType = :issueType', {
issueType: IssueType.AUDIO,
})
.getCount();
const subtitlesCount = await query
.where('issue.issueType = :issueType', {
issueType: IssueType.SUBTITLES,
})
.getCount();
const othersCount = await query
.where('issue.issueType = :issueType', {
issueType: IssueType.OTHER,
})
.getCount();
const openCount = await query
.where('issue.status = :issueStatus', {
issueStatus: IssueStatus.OPEN,
})
.getCount();
const closedCount = await query
.where('issue.status = :issueStatus', {
issueStatus: IssueStatus.RESOLVED,
})
.getCount();
return res.status(200).json({
total: totalCount,
video: videoCount,
audio: audioCount,
subtitles: subtitlesCount,
others: othersCount,
open: openCount,
closed: closedCount,
});
} catch (e) {
logger.debug('Something went wrong retrieving issue counts.', {
label: 'API',
errorMessage: e.message,
});
next({ status: 500, message: 'Unable to retrieve issue counts.' });
}
});
issueRoutes.get<{ issueId: string }>(
'/:issueId',
isAuthenticated(

View File

@@ -110,6 +110,12 @@ const networks: Network[] = [
'https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/nm8d7P7MJNiBLdgIzUK0gkuEA4r.png',
url: '/discover/tv/network/16',
},
{
name: 'Paramount+',
image:
'https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/fi83B1oztoS47xxcemFdPMhIzK.png',
url: '/discover/tv/network/4330',
},
{
name: 'BBC One',
image:

View File

@@ -266,8 +266,11 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
as="a"
buttonType="ghost"
href={
settings.currentSettings.jellyfinHost +
'/web/#!/forgotpassword.html'
process.env.JELLYFIN_TYPE == 'emby'
? settings.currentSettings.jellyfinHost +
'/web/index.html#!/startup/forgotpassword.html'
: settings.currentSettings.jellyfinHost +
'/web/index.html#!/forgotpassword.html'
}
>
{intl.formatMessage(messages.forgotpassword)}

View File

@@ -210,7 +210,7 @@ const ManageSlideOver: React.FC<
{hasPermission(Permission.ADMIN) &&
(data.mediaInfo?.serviceUrl ||
data.mediaInfo?.tautulliUrl ||
watchData?.data?.playCount) && (
!!watchData?.data?.playCount) && (
<div>
<h3 className="mb-2 text-xl font-bold">
{intl.formatMessage(messages.manageModalMedia)}
@@ -325,7 +325,7 @@ const ManageSlideOver: React.FC<
{hasPermission(Permission.ADMIN) &&
(data.mediaInfo?.serviceUrl4k ||
data.mediaInfo?.tautulliUrl4k ||
watchData?.data4k?.playCount) && (
!!watchData?.data4k?.playCount) && (
<div>
<h3 className="mb-2 text-xl font-bold">
{intl.formatMessage(messages.manageModalMedia4k)}

View File

@@ -26,6 +26,7 @@ const messages = defineMessages({
server: 'Destination Server',
profilechanged: 'Quality Profile',
rootfolder: 'Root Folder',
languageprofile: 'Language Profile',
});
interface RequestBlockProps {
@@ -38,7 +39,8 @@ const RequestBlock: React.FC<RequestBlockProps> = ({ request, onUpdate }) => {
const intl = useIntl();
const [isUpdating, setIsUpdating] = useState(false);
const [showEditModal, setShowEditModal] = useState(false);
const { profile, rootFolder, server } = useRequestOverride(request);
const { profile, rootFolder, server, languageProfile } =
useRequestOverride(request);
const updateRequest = async (type: 'approve' | 'decline'): Promise<void> => {
setIsUpdating(true);
@@ -209,7 +211,7 @@ const RequestBlock: React.FC<RequestBlockProps> = ({ request, onUpdate }) => {
</div>
</div>
)}
{(server || profile !== null || rootFolder) && (
{(server || profile || rootFolder || languageProfile) && (
<>
<div className="mt-4 mb-1 text-sm">
{intl.formatMessage(messages.requestoverrides)}
@@ -223,12 +225,12 @@ const RequestBlock: React.FC<RequestBlockProps> = ({ request, onUpdate }) => {
<span>{server}</span>
</li>
)}
{profile !== null && (
{profile && (
<li className="flex justify-between px-1 py-2">
<span className="font-bold">
{intl.formatMessage(messages.profilechanged)}
</span>
<span>ID {profile}</span>
<span>{profile}</span>
</li>
)}
{rootFolder && (
@@ -239,6 +241,14 @@ const RequestBlock: React.FC<RequestBlockProps> = ({ request, onUpdate }) => {
<span>{rootFolder}</span>
</li>
)}
{languageProfile && (
<li className="flex justify-between px-1 py-2">
<span className="mr-2 font-bold">
{intl.formatMessage(messages.languageprofile)}
</span>
<span>{languageProfile}</span>
</li>
)}
</ul>
</>
)}

View File

@@ -82,7 +82,9 @@ const useDiscover = <T extends BaseMedia, S = Record<string, never>>(
const isEmpty = !isLoadingInitialData && titles?.length === 0;
const isReachingEnd =
isEmpty || (!!data && (data[data?.length - 1]?.results.length ?? 0) < 20);
isEmpty ||
(!!data && (data[data?.length - 1]?.results.length ?? 0) < 20) ||
(!!data && (data[data?.length - 1]?.totalResults ?? 0) < 41);
return {
isLoadingInitialData,

View File

@@ -1,45 +1,61 @@
import useSWR from 'swr';
import { MediaRequest } from '../../server/entity/MediaRequest';
import { ServiceCommonServer } from '../../server/interfaces/api/serviceInterfaces';
import {
ServiceCommonServer,
ServiceCommonServerWithDetails,
} from '../../server/interfaces/api/serviceInterfaces';
interface OverrideStatus {
server: string | null;
profile: number | null;
rootFolder: string | null;
server?: string;
profile?: string;
rootFolder?: string;
languageProfile?: string;
}
const useRequestOverride = (request: MediaRequest): OverrideStatus => {
const { data } = useSWR<ServiceCommonServer[]>(
const { data: allServers } = useSWR<ServiceCommonServer[]>(
`/api/v1/service/${request.type === 'movie' ? 'radarr' : 'sonarr'}`
);
if (!data) {
return {
server: null,
profile: null,
rootFolder: null,
};
const { data } = useSWR<ServiceCommonServerWithDetails>(
`/api/v1/service/${request.type === 'movie' ? 'radarr' : 'sonarr'}/${
request.serverId
}`
);
if (!data || !allServers) {
return {};
}
const defaultServer = data.find(
const defaultServer = allServers.find(
(server) => server.is4k === request.is4k && server.isDefault
);
const activeServer = data.find((server) => server.id === request.serverId);
const activeServer = allServers.find(
(server) => server.id === request.serverId
);
return {
server:
activeServer && request.serverId !== defaultServer?.id
? activeServer.name
: null,
: undefined,
profile:
defaultServer?.activeProfileId !== request.profileId
? request.profileId
: null,
? data.profiles.find((profile) => profile.id === request.profileId)
?.name
: undefined,
rootFolder:
defaultServer?.activeDirectory !== request.rootFolder
? request.rootFolder
: null,
: undefined,
languageProfile:
request.type === 'tv' &&
defaultServer?.activeLanguageProfileId !== request.languageProfileId
? data.languageProfiles?.find(
(profile) => profile.id === request.languageProfileId
)?.name
: undefined,
};
};

View File

@@ -1,9 +1,9 @@
/* eslint-disable react-hooks/exhaustive-deps */
import type { UrlObject } from 'url';
import { useEffect, useState, Dispatch, SetStateAction } from 'react';
import useDebouncedState from './useDebouncedState';
import { useRouter } from 'next/router';
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import type { UrlObject } from 'url';
import type { Nullable } from '../utils/typeHelpers';
import useDebouncedState from './useDebouncedState';
type Url = string | UrlObject;
@@ -48,7 +48,7 @@ const useSearchInput = (): SearchObject => {
* in a new route. If we are, then we only replace the history.
*/
useEffect(() => {
if (debouncedValue !== '') {
if (debouncedValue !== '' && searchOpen) {
if (router.pathname.startsWith('/search')) {
router.replace({
pathname: router.pathname,

View File

@@ -269,6 +269,7 @@
"components.QuotaSelector.unlimited": "Unlimited",
"components.RegionSelector.regionDefault": "All Regions",
"components.RegionSelector.regionServerDefault": "Default ({region})",
"components.RequestBlock.languageprofile": "Language Profile",
"components.RequestBlock.profilechanged": "Quality Profile",
"components.RequestBlock.requestoverrides": "Request Overrides",
"components.RequestBlock.rootfolder": "Root Folder",