Compare commits

...

26 Commits

Author SHA1 Message Date
renovate[bot]
23d05a5921 chore(deps): update nextjs monorepo to v14.2.35 2025-12-19 03:10:51 +00:00
fallenbagel
3ee69663dc fix(local-login): remove automatic plex linking and reduce logout log verbosity (#2225)
Removed redundant Plex user discovery logic that applies to all media servers currently. This is now
handled explicitly via linked accounts settings page. Also changed the successful logout log level
from info to debug since its routine behaviour
2025-12-15 19:44:43 +08:00
Ludovic Ortega
539d49879d chore: fix translate badge svg url (#2228)
* chore: fix translate badge svg url

Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>

* fix: use https instead of http

Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>

---------

Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>
2025-12-14 05:37:36 +08:00
RolliePollie18
15356dfe49 fix(jellyfin-scan): reduce jellyfin API calls during recently added scan (#2205)
* fix(jellyfin scanner): reduce jellyfin API calls during recently added scan

Significantly reduce number of API calls, addressing CPU spikes on Jellyfin 10.10+ servers.- Move
getSeasons() call outside the seasons loop (N calls to 1)- Request MediaSources via getEpisodes()
field parameter instead of  individual getItemData() calls per episode (N calls to 1 per season)
Performance improvements (tested on library with 12 TV shows):- Scan duration: 43.7s to 9.1s - Peak
CPU: 277% to 115% - CPU spike duration: 36s to 2s Functionality is unchanged, all availability
statuses identicalbefore and after.

* fix: add getEpisodes overloads to remove unsafe type assertion

* refactor(jellyfin): use generics instead of overloads

---------

Co-authored-by: patrick-acland <patrick.acland@kraken.tech>
2025-12-09 22:20:47 +08:00
fallenbagel
1f04eeb040 fix: disable automatic auth revalidation on auth pages (#2213)
* fix: disable automatic auth revalidation on auth pages

Prevents unnecessary `/api/v1/auth/me` requests on login, setup, and password reset pages.

fix #738

* fix: update regex to include resetpassword guid & add missing condition in refreshInterval
2025-12-09 13:17:17 +01:00
Thibaut Noah
e3028c21f2 docs: add webpush related troubleshooting steps (#2170)
* Update troubleshooting.mdx

Add potential fixes for users who fail to enable their web push notifications

* Update docs/troubleshooting.mdx

Modify appName syntax for better coding norm

Co-authored-by: Gauthier <mail@gauthierth.fr>

* refactor: apply suggestions from review comments

Co-authored-by: Gauthier <mail@gauthierth.fr>

* docs(troubleshooting): fix typos in troubleshooting doc page

---------

Co-authored-by: Gauthier <mail@gauthierth.fr>
Co-authored-by: fallenbagel <98979876+fallenbagel@users.noreply.github.com>
2025-12-09 08:49:42 +00:00
Gauthier
9d8b343790 chore(deps): update all non-major dependencies (#2188)
Update all non-major dependencies. Modifications in `src` files are there to fix linting issues.
2025-12-09 09:40:35 +01:00
fallenbagel
f4fe16608a fix(jellyfin-api): use standard Authorization header (#2211)
Replace X-Emby-Authorization with Authorization header to fix authentication failures when users
have <EnableLegacyAuthorization>false</EnableLegacyAuthorization> in their Jellyfin system.xml.
2025-12-08 15:46:47 +01:00
Ludovic Ortega
d660a540da chore(helm): prepare for release (#2189)
Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>
2025-12-07 17:22:28 +01:00
Ludovic Ortega
48ef2984e5 docs: fix chown command for windows users (#2192)
Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>
2025-12-03 14:39:03 +01:00
Disparate2761
c5fc31c352 docs(buildfromsource): touch up path inconsistencies (#2184) 2025-12-01 14:57:01 +01:00
Ludovic Ortega
c3b9ea6ce4 chore: improve PR template (#2175) 2025-11-28 13:05:47 +01:00
Ludovic Ortega
b66b36186a docs: update weblate links (#2168)
Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>
2025-11-22 23:29:35 +01:00
Ludovic Ortega
fb5196bdec chore: remove CHANGELOG.md (#2169) 2025-11-22 23:05:42 +01:00
0xsysr3ll
bde322de8e fix(override-rules): show correct genres for both *arr services (#2155) 2025-11-21 22:24:14 +01:00
Gauvain
af083a3cd5 chore: rebrand from Jellyseerr to Seerr across project (#2116) 2025-11-18 22:51:20 +01:00
Ludovic Ortega
f4af6ed5f4 docs: add migration guide (#2069) 2025-11-18 11:12:50 +01:00
0xsysr3ll
267450a297 docs: update AI assistance notice link in pull request template (#2154)
Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me>
2025-11-14 16:56:30 +01:00
0xsysr3ll
939000fbe4 ci: update Docker Hub image references in CI workflows (#2153)
Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me>
2025-11-14 16:36:38 +01:00
James Kruger
08800c7cf3 docs: update Kubernetes installation documentation for Seerr (#2126) 2025-11-14 10:57:44 +01:00
0xsysr3ll
2fe72530a2 fix(docker): pass COMMIT_TAG to build stage for custom image builds (#2146)
This PR fixes the issue where custom images built with `--build-arg COMMIT_TAG` would fail because the client bundle didn't receive the commit tag value.

Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me>
2025-11-12 22:50:25 +01:00
Ludovic Ortega
6dcae346f9 fix(docker): casing in dockerfile (#2141) 2025-11-11 17:00:31 +00:00
0xsysr3ll
597858785e fix(ui): ensure mobile media type filter is always visible on actor pages (#2128)
Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me>
2025-11-05 21:27:11 +01:00
Joe Harrison
91aa7d143e ci: bump cosign installer to v4.0.0 (#2127) 2025-11-04 11:33:47 +01:00
Ludovic Ortega
41bcbfe9a4 chore: remove packages section in README (#2124)
Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>
2025-11-03 21:59:36 +01:00
Joe Harrison
7d4b2853dc ci: combined workflows for ai and support (#2113) 2025-10-31 13:12:07 +01:00
56 changed files with 4085 additions and 5984 deletions

View File

@@ -1,14 +1,33 @@
#### Description
<!--
Please read contributing guide before submitting
your pull request. Please fill in each section below to help us better prioritize your pull request. Thanks!
-->
#### Screenshot (if UI-related)
## Description
#### To-Dos
<!--- Describe your changes in detail -->
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
- [ ] Disclosed any use of AI (see our [policy](../CONTRIBUTING.md#ai-assistance-notice))
- Fixes #XXXX
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
## Screenshots / Logs (if applicable)
## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] I have read and followed the contribution [guidelines](https://github.com/seerr-team/seerr/blob/develop/CONTRIBUTING.md).
- [ ] Disclosed any use of AI (see our [policy](https://github.com/seerr-team/seerr/blob/develop/CONTRIBUTING.md#ai-assistance-notice))
- [ ] I have updated the documentation accordingly.
- [ ] All new and existing tests passed.
- [ ] Successful build `pnpm build`
- [ ] Translation keys `pnpm i18n:extract`
- [ ] Database migration (if required)
#### Issues Fixed or Closed
- Fixes #XXXX

View File

@@ -14,6 +14,9 @@ on:
permissions:
contents: read
env:
DOCKER_HUB: seerr/seerr
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
@@ -140,7 +143,7 @@ jobs:
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
with:
images: |
${{ github.repository }}
${{ env.DOCKER_HUB }}
ghcr.io/${{ github.repository }}
tags: |
type=raw,value=develop

View File

@@ -105,7 +105,7 @@ jobs:
uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1.2.4
- name: Install Cosign
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- name: Downloads artifacts
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
@@ -157,7 +157,7 @@ jobs:
persist-credentials: false
- name: Install Cosign
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- name: Downloads artifacts
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0

View File

@@ -11,6 +11,9 @@ on:
permissions:
contents: read
env:
DOCKER_HUB: seerr/seerr
concurrency:
group: preview-${{ github.ref }}
cancel-in-progress: true
@@ -115,7 +118,7 @@ jobs:
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
with:
images: |
${{ github.repository }}
${{ env.DOCKER_HUB }}
ghcr.io/${{ github.repository }}
tags: |
type=raw,value=preview-${{ steps.ver.outputs.version }}

View File

@@ -144,7 +144,7 @@ jobs:
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
with:
images: |
${{ github.repository }}
${{ env.DOCKER_HUB }}
ghcr.io/${{ github.repository }}
tags: |
type=raw,value=${{ env.VERSION }}
@@ -173,7 +173,7 @@ jobs:
- name: Resolve manifest digest
id: digests
run: |
DIGEST=$(docker buildx imagetools inspect "${{ github.repository }}:${{ env.VERSION }}" --format '{{json .Manifest.Digest}}' | tr -d '"')
DIGEST=$(docker buildx imagetools inspect "${{ env.DOCKER_HUB }}:${{ env.VERSION }}" --format '{{json .Manifest.Digest}}' | tr -d '"')
echo "IMAGE_DIGEST=$DIGEST" >> $GITHUB_OUTPUT
- name: Also tag :latest (non-pre-release only)
@@ -206,7 +206,7 @@ jobs:
persist-credentials: false
- name: Install Cosign
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- name: Install Trivy
uses: aquasecurity/setup-trivy@e6c2c5e321ed9123bda567646e2f96565e34abe1 # v0.2.4
@@ -267,7 +267,7 @@ jobs:
VERSION: ${{ github.ref_name }}
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- name: Verify signatures
run: |

View File

@@ -1,20 +1,23 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: 'AI-generated pull requests'
name: 'Seerr Labeller'
on:
pull_request_target:
types: [labeled, unlabeled, reopened]
issues:
types: [labeled, unlabeled, reopened]
permissions:
pull-requests: read
permissions: {}
jobs:
ai-generated-support:
if: github.event.label.name == 'ai-generated' || github.event.action == 'reopened'
if: >
github.event_name == 'pull_request_target' &&
(github.event.label.name == 'ai-generated' || (github.event.action == 'reopened' && contains(github.event.pull_request.labels.*.name, 'ai-generated')))
runs-on: ubuntu-24.04
concurrency:
group: support-${{ github.event.pull_request.number }}
group: ai-generated-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
pull-requests: write
@@ -43,7 +46,7 @@ jobs:
retry gh pr close "$NUMBER" -R "$GH_REPO" || true
gh pr lock "$NUMBER" -R "$GH_REPO" -r "spam" || true
- name: Reopened or label removed, unlock pull request
- name: Label removed, reopen and unlock pull request
if: github.event.action == 'unlabeled' && github.event.label.name == 'ai-generated'
shell: bash
run: |
@@ -57,3 +60,52 @@ jobs:
run: |
gh pr edit "$NUMBER" -R "$GH_REPO" --remove-label "ai-generated" || true
gh pr unlock "$NUMBER" -R "$GH_REPO" || true
support:
if: >
github.event_name == 'issues' &&
(github.event.label.name == 'support' ||
(github.event.action == 'reopened' && contains(github.event.issue.labels.*.name, 'support')))
runs-on: ubuntu-24.04
concurrency:
group: support-${{ github.event.issue.number }}
cancel-in-progress: true
permissions:
issues: write
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.issue.number }}
ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
steps:
- name: Label added, comment and close issue
if: github.event.action == 'labeled' && github.event.label.name == 'support'
shell: bash
env:
BODY: >
:wave: @${{ env.ISSUE_AUTHOR }}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears
to be a support request. Please use our support channels
to get help with Seerr.
- [Discord](https://discord.gg/seerr)
run: |
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
retry gh issue comment "$NUMBER" -R "$GH_REPO" -b "$BODY" || true
retry gh issue close "$NUMBER" -R "$GH_REPO" || true
gh issue lock "$NUMBER" -R "$GH_REPO" -r "off_topic" || true
- name: Label removed, reopen and unlock issue
if: github.event.action == 'unlabeled' && github.event.label.name == 'support'
shell: bash
run: |
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
retry gh issue reopen "$NUMBER" -R "$GH_REPO" || true
gh issue unlock "$NUMBER" -R "$GH_REPO" || true
- name: Remove support label on manual reopen
if: github.event.action == 'reopened'
shell: bash
run: |
gh issue edit "$NUMBER" -R "$GH_REPO" --remove-label "support" || true
gh issue unlock "$NUMBER" -R "$GH_REPO" || true

View File

@@ -1,57 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: 'Support requests'
on:
issues:
types: [labeled, unlabeled, reopened]
permissions:
issues: read
jobs:
support:
if: github.event.label.name == 'support' || github.event.action == 'reopened'
runs-on: ubuntu-24.04
concurrency:
group: support-${{ github.event.issue.number }}
cancel-in-progress: true
permissions:
issues: write
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.issue.number }}
ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
steps:
- name: Label added, comment and close issue
if: github.event.action == 'labeled' && github.event.label.name == 'support'
shell: bash
env:
BODY: >
:wave: @${{ env.ISSUE_AUTHOR }}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears
to be a support request. Please use our support channels
to get help with Seerr.
- [Discord](https://discord.gg/seerr)
run: |
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
retry gh issue comment "$NUMBER" -R "$GH_REPO" -b "$BODY" || true
retry gh issue close "$NUMBER" -R "$GH_REPO" || true
gh issue lock "$NUMBER" -R "$GH_REPO" -r "off_topic" || true
- name: Reopened or label removed, unlock issue
if: github.event.action == 'unlabeled' && github.event.label.name == 'support'
shell: bash
run: |
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
retry gh issue reopen "$NUMBER" -R "$GH_REPO" || true
gh issue unlock "$NUMBER" -R "$GH_REPO" || true
- name: Remove support label on manual reopen
if: github.event.action == 'reopened'
shell: bash
run: |
gh issue edit "$NUMBER" -R "$GH_REPO" --remove-label "support" || true
gh issue unlock "$NUMBER" -R "$GH_REPO" || true

3
.gitignore vendored
View File

@@ -71,3 +71,6 @@ tsconfig.tsbuildinfo
# Config Cache Directory
config/cache
# Docker compose
compose.override.yaml

View File

@@ -2,7 +2,6 @@
.next/
dist/
config/
CHANGELOG.md
pnpm-lock.yaml
cypress/config/settings.cypress.json

File diff suppressed because it is too large Load Diff

View File

@@ -151,9 +151,9 @@ When adding new UI text, please try to adhere to the following guidelines:
## Translation
We use [Weblate](https://jellyseerr.borgcube.de/projects/jellyseerr/jellyseerr-frontend/) for our translations, and your help with localizing Seerr would be greatly appreciated! If your language is not listed below, please [open a feature request](/../../issues/new/choose).
We use [Weblate](https://translate.seerr.dev/projects/seerr/seerr-frontend/) for our translations, and your help with localizing Seerr would be greatly appreciated! If your language is not listed below, please [open a feature request](/../../issues/new/choose).
<a href="https://jellyseerr.borgcube.de/engage/jellysseerr/"><img src="https://jellyseerr.borgcube.de/widget/jellyseerr/multi-auto.svg" alt="Translation status" /></a>
<a href="https://translate.seerr.dev/engage/seerr/"><img src="https://translate.seerr.dev/widget/seerr/multi-auto.svg" alt="Translation status" /></a>
## Migrations

View File

@@ -13,7 +13,10 @@ WORKDIR /app
FROM base AS prod-deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store CI=true pnpm install --prod --frozen-lockfile
FROM base as build
FROM base AS build
ARG COMMIT_TAG
ENV COMMIT_TAG=${COMMIT_TAG}
RUN \
case "${TARGETPLATFORM}" in \

View File

@@ -7,8 +7,8 @@
</p>
<p align="center">
<a href="https://discord.gg/seerr"><img src="https://img.shields.io/discord/783137440809746482" alt="Discord"></a>
<a href="https://hub.docker.com/r/fallenbagel/jellyseerr"><img src="https://img.shields.io/docker/pulls/fallenbagel/jellyseerr" alt="Docker pulls"></a>
<a href="http://translate.jellyseerr.dev/engage/jellyseerr/"><img src="http://translate.jellyseerr.dev/widget/jellyseerr/jellyseerr-frontend/svg-badge.svg" alt="Translation status" /></a>
<a href="https://hub.docker.com/r/seerr/seerr"><img src="https://img.shields.io/docker/pulls/seerr/seerr" alt="Docker pulls"></a>
<a href="https://translate.seerr.dev/engage/seerr/"><img src="https://translate.seerr.dev/widget/seerr/svg-badge.svg" alt="Translation status" /></a>
<a href="https://github.com/seerr-team/seerr/blob/develop/LICENSE"><img alt="GitHub" src="https://img.shields.io/github/license/seerr-team/seerr"></a>
**Seerr** is a free and open source software application for managing requests for your media library. It integrates with the media server of your choice: [Jellyfin](https://jellyfin.org), [Plex](https://plex.tv), and [Emby](https://emby.media/). In addition, it integrates with your existing services, such as **[Sonarr](https://sonarr.tv/)**, **[Radarr](https://radarr.video/)**.
@@ -36,12 +36,6 @@ Check out our documentation for instructions on how to install and run Seerr:
https://docs.seerr.dev/getting-started/
### Packages:
Archlinux: [AUR](https://aur.archlinux.org/packages/jellyseerr)
Nix: [Nixpkg](https://search.nixos.org/packages?channel=unstable&show=jellyseerr)
## Preview
<img src="./public/preview.jpg">

View File

@@ -4,8 +4,8 @@ name: seerr-chart
description: Seerr helm chart for Kubernetes
type: application
version: 3.0.0
# renovate: image=ghcr.io/fallenbagel/jellyseerr
appVersion: '2.7.3'
# renovate: image=ghcr.io/seerr-team/seerr
appVersion: '3.0.0'
maintainers:
- name: Seerr Team
url: https://github.com/orgs/seerr-team/people

View File

@@ -1,6 +1,6 @@
# seerr-chart
![Version: 3.0.0](https://img.shields.io/badge/Version-3.0.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2.7.3](https://img.shields.io/badge/AppVersion-2.7.3-informational?style=flat-square)
![Version: 3.0.0](https://img.shields.io/badge/Version-3.0.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 3.0.0](https://img.shields.io/badge/AppVersion-3.0.0-informational?style=flat-square)
Seerr helm chart for Kubernetes
@@ -22,13 +22,13 @@ Kubernetes: `>=1.23.0-0`
## Installation
Refer to [https://docs.seerr.dev/getting-started/kubernetes](Seerr kubernetes documentation)
Refer to [Seerr kubernetes documentation](https://docs.seerr.dev/getting-started/kubernetes)
## Update Notes
### Updating to 3.0.0
Nothing has changed; we just rebranded the `jellyseerr` Helm chart to `seerr` 🥳.
Nothing has changed; we just rebranded the `jellyseerr` Helm chart to `seerr` 🥳 refer to our [Migration guide](https://docs.seerr.dev/migration-guide).
### Updating to 2.7.0
@@ -70,12 +70,20 @@ If `replicaCount` value was used - remove it. Helm update should work fine after
| nodeSelector | object | `{}` | |
| podAnnotations | object | `{}` | |
| podLabels | object | `{}` | |
| podSecurityContext | object | `{}` | |
| podSecurityContext.fsGroup | int | `1000` | |
| podSecurityContext.fsGroupChangePolicy | string | `"OnRootMismatch"` | |
| probes.livenessProbe | object | `{}` | Configure liveness probe |
| probes.readinessProbe | object | `{}` | Configure readiness probe |
| probes.startupProbe | string | `nil` | Configure startup probe |
| resources | object | `{}` | |
| securityContext | object | `{}` | |
| securityContext.allowPrivilegeEscalation | bool | `false` | |
| securityContext.capabilities.drop[0] | string | `"ALL"` | |
| securityContext.privileged | bool | `false` | |
| securityContext.readOnlyRootFilesystem | bool | `false` | |
| securityContext.runAsGroup | int | `1000` | |
| securityContext.runAsNonRoot | bool | `true` | |
| securityContext.runAsUser | int | `1000` | |
| securityContext.seccompProfile.type | string | `"RuntimeDefault"` | |
| service.port | int | `80` | |
| service.type | string | `"ClusterIP"` | |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account |

View File

@@ -16,13 +16,13 @@
## Installation
Refer to [https://docs.seerr.dev/getting-started/kubernetes](Seerr kubernetes documentation)
Refer to [Seerr kubernetes documentation](https://docs.seerr.dev/getting-started/kubernetes)
## Update Notes
### Updating to 3.0.0
Nothing has changed; we just rebranded the `jellyseerr` Helm chart to `seerr` 🥳.
Nothing has changed; we just rebranded the `jellyseerr` Helm chart to `seerr` 🥳 refer to our [Migration guide](https://docs.seerr.dev/migration-guide).
### Updating to 2.7.0

View File

@@ -50,16 +50,22 @@ serviceAccount:
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
# fsGroup: 2000
podSecurityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
runAsNonRoot: true
privileged: false
runAsUser: 1000
runAsGroup: 1000
seccompProfile:
type: RuntimeDefault
service:
type: ClusterIP

View File

@@ -24,10 +24,9 @@ import TabItem from '@theme/TabItem';
```bash
sudo mkdir -p /opt/seerr && cd /opt/seerr
```
2. Clone the Seerr repository and checkout the develop branch:
2. Clone the Seerr repository and checkout the main branch:
```bash
git clone https://github.com/fallenbagel/jellyseerr.git
cd jellyseerr
git clone https://github.com/seerr-team/seerr.git .
git checkout main
```
3. Install the dependencies:
@@ -199,9 +198,9 @@ pm2 status seerr
mkdir C:\seerr
cd C:\seerr
```
2. Clone the Seerr repository and checkout the develop branch:
2. Clone the Seerr repository and checkout the main branch:
```powershell
git clone https://github.com/fallenbagel/jellyseerr.git .
git clone https://github.com/seerr-team/seerr.git .
git checkout main
```
3. Install the dependencies:

View File

@@ -18,7 +18,7 @@ An alternative Docker image is available on Docker Hub for this project. You can
:::info
All official Seerr images are cryptographically signed and include a verified [Software Bill of Materials (SBOM)](https://cyclonedx.org/).
To confirm that the container image you are using is authentic and unmodified, please refer to the [Verifying Signed Artifacts](/using-jellyseerr/advanced/verifying-signed-artifacts#verifying-signed-images) guide.
To confirm that the container image you are using is authentic and unmodified, please refer to the [Verifying Signed Artifacts](/using-seerr/advanced/verifying-signed-artifacts#verifying-signed-images) guide.
:::
## Unix (Linux, macOS)

View File

@@ -1,6 +1,6 @@
---
title: Kubernetes (Advanced)
description: Install Jellyseerr in Kubernetes
description: Install Seerr in Kubernetes
sidebar_position: 3
---
# Kubernetes
@@ -11,17 +11,16 @@ This method is not recommended for most users. It is intended for advanced users
:::info
All official Seerr charts are cryptographically signed and include a verified [Software Bill of Materials (SBOM)](https://cyclonedx.org/).
To confirm that the chart you are using is authentic and unmodified, please refer to the [Verifying Signed Artifacts](/using-jellyseerr/advanced/verifying-signed-artifacts#verifying-signed-helm-charts) guide.
To confirm that the chart you are using is authentic and unmodified, please refer to the [Verifying Signed Artifacts](/using-seerr/advanced/verifying-signed-artifacts#verifying-signed-helm-charts) guide.
:::
## Installation
```console
helm install seerr oci://ghcr.io/seerr-team/seerr/seerr-chart
```
Helm values can be found in the Jellyseerr repository under [charts/jellyseerr-chart/README.md](https://github.com/fallenbagel/jellyseerr/tree/develop/charts/jellyseerr-chart).
Helm values can be found in the Seerr repository under [charts/seerr-chart/README.md](https://github.com/seerr-team/seerr/tree/develop/charts/seerr-chart).
Verify the signature with [cosign](https://docs.sigstore.dev/cosign/system_config/installation/) (replace [tag], with the TAG you want to verify) :
```console
cosign verify ghcr.io/seerr-team/seerr/seerr-chart:[tag] --certificate-identity=https://github.com/fallenbagel/jellyseerr/.github/workflows/helm.yml@refs/heads/main --certificate-oidc-issuer=https://token.ac
tions.githubusercontent.com
cosign verify ghcr.io/seerr-team/seerr/seerr-chart:[tag] --certificate-identity=https://github.com/seerr-team/seerr/.github/workflows/helm.yml@refs/heads/main --certificate-oidc-issuer=https://token.actions.githubusercontent.com
```

168
docs/migration-guide.mdx Normal file
View File

@@ -0,0 +1,168 @@
---
title: Migration guide
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Whether you come from Overseerr or Jellyseerr, you don't need to perform any manual migration steps, your instance will automatically be migrated to Seerr.
This migration will run automatically the first time you start your instance using the Seerr codebase (Docker image or source build or Kubernetes, etc.).
An additional migration will happen for Overseerr users, to migrate their configuration to the new codebase.
:::warning
Before doing anything you should backup your existing instance so that you can rollback in case something goes wrong.
See [Backups](/using-seerr/backups) for details on how to properly backup your instance.
:::
## Docker
Refer to [Seerr Docker Documentation](/getting-started/docker), all of our examples have been updated to reflect the below change.
Changes :
- Renamed all references from `overseerr` or `jellyseerr` to `seerr`.
- The container image reference has been updated.
- The container can now be run as a non-root user (`node` user); remove the `user` directive if you have configured it.
- The container no longer provides an init process, so you must configure it by adding `init: true` for Docker Compose or `--init` for the Docker CLI.
:::info
**Config folder permissions**: Since the container now runs as the `node` user (UID 1000), you must ensure your config folder has the correct permissions. The `node` user must have read and write access to the `/app/config` directory.
If you're migrating from a previous installation, you may need to update the ownership of your config folder:
```bash
docker run --rm -v /path/to/appdata/config:/data alpine chown -R 1000:1000 /data
```
This ensures the `node` user (UID 1000) owns the config directory and can read and write to it.
:::
### Unix
Summary of changes :
<Tabs groupId="docker-methods" queryString>
<TabItem value="docker-compose" label="Docker compose">
```yaml {3-6}
---
services:
seerr:
image: ghcr.io/seerr-team/seerr:latest
init: true
container_name: seerr
environment:
- LOG_LEVEL=debug
- TZ=Asia/Tashkent
- PORT=5055 #optional
ports:
- 5055:5055
volumes:
- /path/to/appdata/config:/app/config
healthcheck:
test: wget --no-verbose --tries=1 --spider http://localhost:5055/api/v1/status || exit 1
start_period: 20s
timeout: 3s
interval: 15s
retries: 3
restart: unless-stopped
```
</TabItem>
<TabItem value="docker-cli" label="Docker CLI">
```bash {2-3,10}
docker run -d \
--name seerr \
--init \
-e LOG_LEVEL=debug \
-e TZ=Asia/Tashkent \
-e PORT=5055 \
-p 5055:5055 \
-v /path/to/appdata/config:/app/config \
--restart unless-stopped \
ghcr.io/seerr-team/seerr:latest
```
</TabItem>
</Tabs>
### Windows
Summary of changes :
<Tabs groupId="docker-methods" queryString>
<TabItem value="docker-compose" label="Docker compose">
```yaml {3-6,13,23}
---
services:
seerr:
image: ghcr.io/seerr-team/seerr:latest
init: true
container_name: seerr
environment:
- LOG_LEVEL=debug
- TZ=Asia/Tashkent
ports:
- 5055:5055
volumes:
- seerr-data:/app/config
healthcheck:
test: wget --no-verbose --tries=1 --spider http://localhost:5055/api/v1/status || exit 1
start_period: 20s
timeout: 3s
interval: 15s
retries: 3
restart: unless-stopped
volumes:
seerr-data:
external: true
```
</TabItem>
<TabItem value="docker-cli" label="Docker CLI">
```bash {2-3,8,10}
docker run -d \
--name seerr \
--init \
-e LOG_LEVEL=debug \
-e TZ=Asia/Tashkent \
-e PORT=5055 \
-p 5055:5055 \
-v seerr-data:/app/config \
--restart unless-stopped \
ghcr.io/seerr-team/seerr:latest
```
</TabItem>
</Tabs>
## Kubernetes
Refer to [Seerr Kubernetes Documentation](/getting-started/kubernetes), all of our examples have been updated to reflect the below change.
Changes :
- All references to `jellyseerr` have been renamed to `seerr` in the manifests.
- The container image reference has been updated.
- The default `securityContext` and `podSecurityContext` have been updated to support running the container without root permissions.
Summary of changes :
<Tabs groupId="kubernetes-values" queryString>
<TabItem value="old" label="Old values">
```yaml
image:
repository: fallenbagel/jellyseerr
podSecurityContext: {}
securityContext: {}
```
</TabItem>
<TabItem value="new" label="New values">
```yaml
image:
repository: seerr-team/seerr
podSecurityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
runAsNonRoot: true
privileged: false
runAsUser: 1000
runAsGroup: 1000
seccompProfile:
type: RuntimeDefault
```
</TabItem>
</Tabs>

View File

@@ -174,4 +174,36 @@ This can happen if you have a new installation of Jellyfin/Emby or if you have c
This process should restore your admin privileges while preserving your settings.
## Failed to enable web push notifications
### Option 1: You are using Pi-hole
When using Pi-hole, you need to whitelist the proper domains in order for the queries to not be intercepted and blocked by Pi-hole.
If you are using a chromium based browser (eg: Chrome, Brave, Edge...), the domain you need to whitelist is `fcm.googleapis.com`
If you are using Firefox, the domain you need to whitelist is `push.services.mozilla.com`
1. Log into your Pi-hole through the admin interface, then click on Domains situated under GROUP MANAGEMENT.
2. Add the domain corresponding to your browser in the `Domain to be added` field and then click on Add to allowed domains.
3. Now in order for those changes to be used you need to flush your current dns cache.
4. You can do so by using this command line in your Pi-hole terminal:
```bash
pihole restartdns
```
If this command fails (which is unlikely), use this equivalent:
```bash
pihole -f && pihole restartdns
```
5. Then restart your Seerr instance and try to enable the web push notifications again.
### Option 2: You are using Brave browser
Brave is a "De-Googled" browser. So by default or if you refused a prompt in the past, it cuts the access to the FCM (Firebase Cloud Messaging) service, which is mandatory for the web push notifications on Chromium based browsers.
1. Open Brave and paste this address in the url bar: `brave://settings/privacy`
2. Look for the option: "Use Google services for push messaging"
3. Activate this option
4. Relaunch Brave completely
5. You should now see the notifications prompt appearing instead of an error message.
If you still encounter issues, please reach out on our support channels.

View File

@@ -22,4 +22,4 @@ Users can customize their notification preferences in their own user notificatio
## Requesting New Notification Agents
If we do not currently support your preferred notification agent, feel free to [submit a feature request on GitHub](https://github.com/fallenbagel/jellyseerr/issues). However, please be sure to search first and confirm that there is not already an existing request for the agent!
If we do not currently support your preferred notification agent, feel free to [submit a feature request on GitHub](https://github.com/seerr-team/seerr/issues). However, please be sure to search first and confirm that there is not already an existing request for the agent!

View File

@@ -16,7 +16,7 @@ User notifications are separate from system notifications, and the available not
### Application/API Token
[Register an application](https://pushover.net/apps/build) and enter the API token in this field. (You can use one of the [official icons in our GitHub repository](https://github.com/fallenbagel/jellyseerr/tree/develop/public) when configuring the application.)
[Register an application](https://pushover.net/apps/build) and enter the API token in this field. (You can use one of the [official icons in our GitHub repository](https://github.com/seerr-team/seerr/tree/develop/public) when configuring the application.)
For more details on registering applications or the API token, please see the [Pushover API documentation](https://pushover.net/api#registration).

View File

@@ -1,24 +0,0 @@
---
title: Welcome to the Jellyseerr Blog
description: The official Jellyseerr blog for release notes, technical updates, and community news.
slug: welcome
authors: [fallenbagel, gauthier-th]
tags: [announcement, jellyseerr, blog]
image: https://raw.githubusercontent.com/fallenbagel/jellyseerr/refs/heads/develop/gen-docs/static/img/logo.svg
hide_table_of_contents: false
---
We are pleased to introduce the official Jellyseerr blog.
This space will serve as the central place for:
- Release announcements
- Updates on new features and improvements
- Technical articles, such as details on our [**DNS caching package**](https://github.com/jellyseerr/dns-caching) and other enhancements
- Community-related news
<!--truncate-->
Our goal is to keep the community informed and provide deeper insights into the ongoing development of Jellyseerr.
Thank you for being part of the Jellyseerr project. More updates will follow soon.

View File

@@ -0,0 +1,24 @@
---
title: Welcome to the Seerr Blog
description: The official Seerr blog for release notes, technical updates, and community news.
slug: welcome
authors: [fallenbagel, gauthier-th]
tags: [announcement, seerr, blog]
image: https://raw.githubusercontent.com/seerr-team/seerr/refs/heads/develop/gen-docs/static/img/logo.svg
hide_table_of_contents: false
---
We are pleased to introduce the official Seerr blog.
This space will serve as the central place for:
- Release announcements
- Updates on new features and improvements
- Technical articles, such as details on our [**DNS caching package**](https://github.com/seerr/dns-caching) and other enhancements
- Community-related news
<!--truncate-->
Our goal is to keep the community informed and provide deeper insights into the ongoing development of Seerr.
Thank you for being part of the Seerr project. More updates will follow soon.

View File

@@ -2,7 +2,7 @@
"name": "seerr",
"version": "0.1.0",
"private": true,
"packageManager": "pnpm@10.17.1",
"packageManager": "pnpm@10.24.0",
"scripts": {
"preinstall": "npx only-allow pnpm",
"postinstall": "node postinstall-win.js",
@@ -33,38 +33,38 @@
},
"license": "MIT",
"dependencies": {
"@dr.pogodin/csurf": "^1.14.1",
"@formatjs/intl-displaynames": "6.2.6",
"@dr.pogodin/csurf": "^1.16.6",
"@formatjs/intl-displaynames": "6.8.13",
"@formatjs/intl-locale": "3.1.1",
"@formatjs/intl-pluralrules": "5.1.10",
"@formatjs/intl-pluralrules": "5.4.6",
"@formatjs/intl-utils": "3.8.4",
"@formatjs/swc-plugin-experimental": "^0.4.0",
"@headlessui/react": "1.7.12",
"@heroicons/react": "2.0.16",
"@heroicons/react": "2.2.0",
"@supercharge/request-ip": "1.2.0",
"@svgr/webpack": "6.5.1",
"@tanem/react-nprogress": "5.0.30",
"@tanem/react-nprogress": "5.0.56",
"@types/ua-parser-js": "^0.7.36",
"@types/wink-jaro-distance": "^2.0.2",
"ace-builds": "1.15.2",
"axios": "1.10.0",
"axios-rate-limit": "1.3.0",
"ace-builds": "1.43.4",
"axios": "1.13.2",
"axios-rate-limit": "1.4.0",
"bcrypt": "5.1.0",
"bowser": "2.11.0",
"bowser": "2.13.1",
"connect-typeorm": "1.1.4",
"cookie-parser": "1.4.7",
"copy-to-clipboard": "3.3.3",
"country-flag-icons": "1.5.5",
"country-flag-icons": "1.6.4",
"cronstrue": "2.23.0",
"date-fns": "2.29.3",
"dayjs": "1.11.7",
"dayjs": "1.11.19",
"dns-caching": "^0.2.7",
"email-templates": "12.0.1",
"email-templates": "12.0.3",
"express": "4.21.2",
"express-openapi-validator": "4.13.8",
"express-rate-limit": "6.7.0",
"express-session": "1.17.3",
"formik": "^2.4.6",
"express-session": "1.18.2",
"formik": "^2.4.9",
"gravatar-url": "3.1.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
@@ -76,19 +76,19 @@
"node-schedule": "2.1.1",
"nodemailer": "6.10.0",
"openpgp": "5.11.2",
"pg": "8.11.0",
"pg": "8.16.3",
"plex-api": "5.3.2",
"pug": "3.0.3",
"react": "^18.3.1",
"react-ace": "10.1.0",
"react-animate-height": "2.1.2",
"react-aria": "3.23.0",
"react-aria": "3.44.0",
"react-dom": "^18.3.1",
"react-intersection-observer": "9.4.3",
"react-intl": "^6.6.8",
"react-markdown": "8.0.5",
"react-popper-tooltip": "4.4.2",
"react-select": "5.7.0",
"react-select": "5.10.2",
"react-spring": "9.7.1",
"react-tailwindcss-datepicker-sct": "1.3.4",
"react-toast-notifications": "2.5.1",
@@ -97,19 +97,19 @@
"react-use-clipboard": "1.0.9",
"reflect-metadata": "0.1.13",
"secure-random-password": "0.2.3",
"semver": "7.7.1",
"semver": "7.7.3",
"sharp": "^0.33.4",
"sqlite3": "5.1.7",
"swagger-ui-express": "4.6.2",
"swr": "2.2.5",
"swr": "2.3.7",
"tailwind-merge": "^2.6.0",
"typeorm": "0.3.12",
"ua-parser-js": "^1.0.35",
"undici": "^7.3.0",
"validator": "^13.15.15",
"web-push": "3.5.0",
"undici": "^7.16.0",
"validator": "^13.15.23",
"web-push": "3.6.7",
"wink-jaro-distance": "^2.0.0",
"winston": "3.8.2",
"winston": "3.18.3",
"winston-daily-rotate-file": "4.7.1",
"xml2js": "0.4.23",
"yamljs": "0.3.0",
@@ -123,32 +123,33 @@
"@tailwindcss/forms": "0.5.10",
"@tailwindcss/typography": "0.5.16",
"@types/bcrypt": "5.0.0",
"@types/cookie-parser": "1.4.3",
"@types/country-flag-icons": "1.2.0",
"@types/csurf": "1.11.2",
"@types/cookie-parser": "1.4.10",
"@types/country-flag-icons": "1.2.2",
"@types/csurf": "1.11.5",
"@types/email-templates": "8.0.4",
"@types/express": "4.17.17",
"@types/express-session": "1.17.6",
"@types/lodash": "4.14.191",
"@types/express-session": "1.18.2",
"@types/lodash": "4.17.21",
"@types/mime": "3",
"@types/node": "22.10.5",
"@types/node-schedule": "2.1.0",
"@types/node-schedule": "2.1.8",
"@types/nodemailer": "6.4.7",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-transition-group": "4.4.5",
"@types/react-transition-group": "4.4.12",
"@types/secure-random-password": "0.2.1",
"@types/semver": "7.3.13",
"@types/swagger-ui-express": "4.1.3",
"@types/validator": "^13.15.3",
"@types/web-push": "3.3.2",
"@types/semver": "7.7.1",
"@types/swagger-ui-express": "4.1.8",
"@types/validator": "^13.15.10",
"@types/web-push": "3.6.4",
"@types/xml2js": "0.4.11",
"@types/yamljs": "0.2.31",
"@types/yup": "0.29.14",
"@typescript-eslint/eslint-plugin": "5.54.0",
"@typescript-eslint/parser": "5.54.0",
"autoprefixer": "10.4.13",
"commitizen": "4.3.0",
"autoprefixer": "10.4.22",
"baseline-browser-mapping": "^2.8.32",
"commitizen": "4.3.1",
"copyfiles": "2.4.1",
"cy-mobile-commands": "0.3.0",
"cypress": "14.1.0",
@@ -157,22 +158,22 @@
"eslint-config-next": "^14.2.4",
"eslint-config-prettier": "8.6.0",
"eslint-plugin-formatjs": "4.9.0",
"eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-no-relative-import-paths": "1.5.2",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-no-relative-import-paths": "1.6.1",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "4.6.0",
"husky": "8.0.3",
"lint-staged": "13.1.2",
"nodemon": "3.1.9",
"postcss": "8.4.31",
"nodemon": "3.1.11",
"postcss": "8.5.6",
"prettier": "2.8.4",
"prettier-plugin-organize-imports": "3.2.2",
"prettier-plugin-tailwindcss": "0.2.3",
"tailwindcss": "3.2.7",
"ts-node": "10.9.1",
"tsc-alias": "1.8.2",
"tsconfig-paths": "4.1.2",
"ts-node": "10.9.2",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
"typescript": "4.9.5"
},
"engines": {
@@ -181,7 +182,7 @@
},
"overrides": {
"sqlite3/node-gyp": "8.4.1",
"@types/express-session": "1.17.6"
"@types/express-session": "1.18.2"
},
"config": {
"commitizen": {
@@ -204,8 +205,11 @@
},
"pnpm": {
"onlyBuiltDependencies": [
"sqlite3",
"bcrypt"
"@swc/core",
"bcrypt",
"cypress",
"sharp",
"sqlite3"
]
}
}

8003
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -112,6 +112,10 @@ export interface JellyfinLibraryItemExtended extends JellyfinLibraryItem {
DateCreated?: string;
}
type EpisodeReturn<T> = T extends { includeMediaInfo: true }
? JellyfinLibraryItemExtended[]
: JellyfinLibraryItem[];
export interface JellyfinItemsReponse {
Items: JellyfinLibraryItemExtended[];
TotalRecordCount: number;
@@ -145,7 +149,7 @@ class JellyfinAPI extends ExternalAPI {
{},
{
headers: {
'X-Emby-Authorization': authHeaderVal,
Authorization: authHeaderVal,
'Content-Type': 'application/json',
Accept: 'application/json',
},
@@ -415,13 +419,22 @@ class JellyfinAPI extends ExternalAPI {
}
}
public async getEpisodes(
public async getEpisodes<
T extends { includeMediaInfo?: boolean } | undefined = undefined
>(
seriesID: string,
seasonID: string
): Promise<JellyfinLibraryItem[]> {
seasonID: string,
options?: T
): Promise<EpisodeReturn<T>> {
try {
const episodeResponse = await this.get<any>(
`/Shows/${seriesID}/Episodes?seasonId=${seasonID}`
`/Shows/${seriesID}/Episodes`,
{
params: {
seasonId: seasonID,
...(options?.includeMediaInfo && { fields: 'MediaSources' }),
},
}
);
return episodeResponse.Items.filter(

View File

@@ -374,9 +374,10 @@ class JellyfinScanner {
) ?? []
).length;
const jellyfinSeasons = await this.jfClient.getSeasons(Id);
for (const season of seasons) {
const JellyfinSeasons = await this.jfClient.getSeasons(Id);
const matchedJellyfinSeason = JellyfinSeasons.find((md) => {
const matchedJellyfinSeason = jellyfinSeasons.find((md) => {
if (tvdbSeasonFromAnidb) {
// In AniDB we don't have the concept of seasons,
// we have multiple shows with only Season 1 (and sometimes a season with index 0 for specials).
@@ -397,38 +398,52 @@ class JellyfinScanner {
// Check if we found the matching season and it has all the available episodes
if (matchedJellyfinSeason) {
// If we have a matched Jellyfin season, get its children metadata so we can check details
const episodes = await this.jfClient.getEpisodes(
Id,
matchedJellyfinSeason.Id
);
//Get count of episodes that are HD and 4K
let totalStandard = 0;
let total4k = 0;
//use for loop to make sure this loop _completes_ in full
//before the next section
for (const episode of episodes) {
let episodeCount = 1;
if (!this.enable4kShow) {
const episodes = await this.jfClient.getEpisodes(
Id,
matchedJellyfinSeason.Id
);
// count number of combined episodes
if (
episode.IndexNumber !== undefined &&
episode.IndexNumberEnd !== undefined
) {
episodeCount =
episode.IndexNumberEnd - episode.IndexNumber + 1;
}
for (const episode of episodes) {
let episodeCount = 1;
// count number of combined episodes
if (
episode.IndexNumber !== undefined &&
episode.IndexNumberEnd !== undefined
) {
episodeCount =
episode.IndexNumberEnd - episode.IndexNumber + 1;
}
if (!this.enable4kShow) {
totalStandard += episodeCount;
} else {
const ExtendedEpisodeData = await this.jfClient.getItemData(
episode.Id
);
}
} else {
// 4K detection enabled - request media info to check resolution
const episodes = await this.jfClient.getEpisodes(
Id,
matchedJellyfinSeason.Id,
{ includeMediaInfo: true }
);
ExtendedEpisodeData?.MediaSources?.some((MediaSource) => {
for (const episode of episodes) {
let episodeCount = 1;
// count number of combined episodes
if (
episode.IndexNumber !== undefined &&
episode.IndexNumberEnd !== undefined
) {
episodeCount =
episode.IndexNumberEnd - episode.IndexNumber + 1;
}
// MediaSources field is included in response when includeMediaInfo is true
// We iterate all MediaSources to detect if episode has both standard AND 4K versions
episode.MediaSources?.some((MediaSource) => {
return MediaSource.MediaStreams.some((MediaStream) => {
if (MediaStream.Type === 'Video') {
if ((MediaStream.Width ?? 0) >= 2000) {

View File

@@ -626,76 +626,6 @@ authRoutes.post('/local', async (req, res, next) => {
});
}
const mainUser = await userRepository.findOneOrFail({
select: { id: true, plexToken: true, plexId: true },
where: { id: 1 },
});
const mainPlexTv = new PlexTvAPI(mainUser.plexToken ?? '');
if (!user.plexId) {
try {
const plexUsersResponse = await mainPlexTv.getUsers();
const account = plexUsersResponse.MediaContainer.User.find(
(account) =>
account.$.email &&
account.$.email.toLowerCase() === user.email.toLowerCase()
)?.$;
if (
account &&
(await mainPlexTv.checkUserAccess(parseInt(account.id)))
) {
logger.info(
'Found matching Plex user; updating user with Plex data',
{
label: 'API',
ip: req.ip,
email: body.email,
userId: user.id,
plexId: account.id,
plexUsername: account.username,
}
);
user.plexId = parseInt(account.id);
user.avatar = account.thumb;
user.email = account.email;
user.plexUsername = account.username;
user.userType = UserType.PLEX;
await userRepository.save(user);
}
} catch (e) {
logger.error('Something went wrong fetching Plex users', {
label: 'API',
errorMessage: e.message,
});
}
}
if (
user.plexId &&
user.plexId !== mainUser.plexId &&
!(await mainPlexTv.checkUserAccess(user.plexId))
) {
logger.warn(
'Failed sign-in attempt from Plex user without access to the media server',
{
label: 'API',
account: {
ip: req.ip,
email: body.email,
userId: user.id,
plexId: user.plexId,
},
}
);
return next({
status: 403,
message: 'Access denied.',
});
}
// Set logged in session
if (user && req.session) {
req.session.userId = user.id;
@@ -775,7 +705,7 @@ authRoutes.post('/logout', async (req, res, next) => {
});
return next({ status: 500, message: 'Failed to destroy session.' });
}
logger.info('Successfully logged out user', {
logger.debug('Successfully logged out user', {
label: 'Auth',
userId,
});

View File

@@ -354,7 +354,7 @@ const BlacklistedItem = ({ item, revalidateList }: BlacklistedItemProps) => {
src={
title?.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${title.posterPath}`
: '/images/jellyseerr_poster_not_found.png'
: '/images/seerr_poster_not_found.png'
}
alt=""
sizes="100vw"

View File

@@ -233,7 +233,7 @@ const CollectionDetails = ({ collection }: CollectionDetailsProps) => {
src={
data.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${data.posterPath}`
: '/images/jellyseerr_poster_not_found.png'
: '/images/seerr_poster_not_found.png'
}
alt=""
sizes="100vw"

View File

@@ -25,7 +25,7 @@ const LabeledCheckbox: React.FC<LabeledCheckboxProps> = ({
<Field type="checkbox" id={id} name={id} onChange={onChange} />
</div>
<div className="ml-3 text-sm leading-6">
<label htmlFor="localLogin" className="block">
<label htmlFor="localLogin" className="block" aria-label={label}>
<div className="flex flex-col">
<span className="font-medium text-white">{label}</span>
<span className="font-normal text-gray-400">{description}</span>

View File

@@ -232,7 +232,7 @@ const IssueDetails = () => {
src={
data.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${data.posterPath}`
: '/images/jellyseerr_poster_not_found.png'
: '/images/seerr_poster_not_found.png'
}
alt=""
sizes="100vw"

View File

@@ -151,7 +151,7 @@ const IssueItem = ({ issue }: IssueItemProps) => {
src={
title.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${title.posterPath}`
: '/images/jellyseerr_poster_not_found.png'
: '/images/seerr_poster_not_found.png'
}
alt=""
sizes="100vw"

View File

@@ -490,7 +490,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
src={
data.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${data.posterPath}`
: '/images/jellyseerr_poster_not_found.png'
: '/images/seerr_poster_not_found.png'
}
alt=""
sizes="100vw"

View File

@@ -46,7 +46,7 @@ const NotificationType = ({
/>
</div>
<div className="ml-3 text-sm leading-6">
<label htmlFor={option.id} className="block">
<label htmlFor={option.id} className="block" aria-label={option.name}>
<div className="flex flex-col">
<span className="font-medium text-white">{option.name}</span>
<span className="font-normal text-gray-400">

View File

@@ -123,7 +123,7 @@ const PermissionOption = ({
/>
</div>
<div className="ml-3 text-sm leading-6">
<label htmlFor={option.id} className="block">
<label htmlFor={option.id} className="block" aria-label={option.name}>
<div className="flex flex-col">
<span className="font-medium text-white">{option.name}</span>
<span className="font-normal text-gray-400">

View File

@@ -292,6 +292,7 @@ const PersonDetails = () => {
</div>
)}
</div>
<div className="lg:hidden">{mediaTypePicker}</div>
{data.biography && (
<div className="relative text-left">
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
@@ -314,7 +315,6 @@ const PersonDetails = () => {
)}
</div>
</div>
<div className="lg:hidden">{mediaTypePicker}</div>
{data.knownForDepartment === 'Acting' ? [cast, crew] : [crew, cast]}
{isLoading && <LoadingSpinner />}
</>

View File

@@ -617,7 +617,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
src={
title.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${title.posterPath}`
: '/images/jellyseerr_poster_not_found.png'
: '/images/seerr_poster_not_found.png'
}
alt=""
sizes="100vw"

View File

@@ -440,7 +440,7 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
src={
title.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${title.posterPath}`
: '/images/jellyseerr_poster_not_found.png'
: '/images/seerr_poster_not_found.png'
}
alt=""
sizes="100vw"

View File

@@ -450,7 +450,7 @@ const CollectionRequestModal = ({
src={
part.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${part.posterPath}`
: '/images/jellyseerr_poster_not_found.png'
: '/images/seerr_poster_not_found.png'
}
alt=""
sizes="100vw"

View File

@@ -92,8 +92,7 @@ const SearchByNameModal = ({
<CachedImage
type="tvdb"
src={
item.remotePoster ??
'/images/jellyseerr_poster_not_found.png'
item.remotePoster ?? '/images/seerr_poster_not_found.png'
}
alt={item.title}
className="w-100 h-auto rounded-md"

View File

@@ -12,7 +12,6 @@ import type {
TmdbGenre,
TmdbKeywordSearchResponse,
} from '@server/api/themoviedb/interfaces';
import type { GenreSliderItem } from '@server/interfaces/api/discoverInterfaces';
import type { UserResultsResponse } from '@server/interfaces/api/userInterfaces';
import type {
Keyword,
@@ -185,9 +184,7 @@ export const GenreSelector = ({
}, [defaultValue, type]);
const loadGenreOptions = async (inputValue: string) => {
const results = await axios.get<GenreSliderItem[]>(
`/api/v1/discover/genreslider/${type}`
);
const results = await axios.get<TmdbGenre[]>(`/api/v1/genres/${type}`);
return results.data
.map((result) => ({
@@ -201,7 +198,7 @@ export const GenreSelector = ({
return (
<AsyncSelect
key={`genre-select-${defaultDataValue}`}
key={`genre-select-${type}-${defaultDataValue}`}
className="react-select-container"
classNamePrefix="react-select"
defaultValue={isMulti ? defaultDataValue : defaultDataValue?.[0]}

View File

@@ -288,7 +288,7 @@ const NotificationsWebhook = () => {
{values.supportVariables && (
<div className="mt-2">
<Link
href="https://docs.seerr.dev/using-jellyseerr/notifications/webhook#template-variables"
href="https://docs.seerr.dev/using-seerr/notifications/webhook#template-variables"
passHref
legacyBehavior
>
@@ -376,7 +376,7 @@ const NotificationsWebhook = () => {
<span>{intl.formatMessage(messages.resetPayload)}</span>
</Button>
<Link
href="https://docs.seerr.dev/using-jellyseerr/notifications/webhook#template-variables"
href="https://docs.seerr.dev/using-seerr/notifications/webhook#template-variables"
passHref
legacyBehavior
>

View File

@@ -337,7 +337,13 @@ const OverrideRuleModal = ({
<div className="form-input-area">
<div className="form-input-field">
<GenreSelector
type={values.radarrServiceId ? 'movie' : 'tv'}
type={
values.radarrServiceId != null
? 'movie'
: values.sonarrServiceId != null
? 'tv'
: 'tv'
}
defaultValue={values.genre}
isMulti
isDisabled={!isValidated || isTesting}

View File

@@ -335,7 +335,7 @@ const TitleCard = ({
src={
image
? `https://image.tmdb.org/t/p/w300_and_h450_face${image}`
: `/images/jellyseerr_poster_not_found_logo_top.png`
: `/images/seerr_poster_not_found_logo_top.png`
}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
fill

View File

@@ -532,7 +532,7 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
src={
data.posterPath
? `https://image.tmdb.org/t/p/w600_and_h900_bestv2${data.posterPath}`
: '/images/jellyseerr_poster_not_found.png'
: '/images/seerr_poster_not_found.png'
}
alt=""
sizes="100vw"

View File

@@ -2,6 +2,7 @@ import { UserType } from '@server/constants/user';
import type { PermissionCheckOptions } from '@server/lib/permissions';
import { hasPermission, Permission } from '@server/lib/permissions';
import type { NotificationAgentKey } from '@server/lib/settings';
import { useRouter } from 'next/router';
import type { MutatorCallback } from 'swr';
import useSWR from 'swr';
@@ -56,13 +57,21 @@ export const useUser = ({
id,
initialData,
}: { id?: number; initialData?: User } = {}): UserHookResponse => {
const router = useRouter();
const isAuthPage = /^\/(login|setup|resetpassword(?:\/|$))/.test(
router.pathname
);
const {
data,
error,
mutate: revalidate,
} = useSWR<User>(id ? `/api/v1/user/${id}` : `/api/v1/auth/me`, {
fallbackData: initialData,
refreshInterval: 30000,
refreshInterval: !isAuthPage ? 30000 : 0,
revalidateOnFocus: !isAuthPage,
revalidateOnMount: !isAuthPage,
revalidateOnReconnect: !isAuthPage,
errorRetryInterval: 30000,
shouldRetryOnError: false,
});

View File

@@ -63,7 +63,7 @@ class PlexOAuth {
'X-Plex-Client-Identifier': clientId,
'X-Plex-Model': 'Plex OAuth',
'X-Plex-Platform': browser.getBrowserName(),
'X-Plex-Platform-Version': browser.getBrowserVersion(),
'X-Plex-Platform-Version': browser.getBrowserVersion() || 'Unknown',
'X-Plex-Device': browser.getOSName(),
'X-Plex-Device-Name': `${browser.getBrowserName()} (Seerr)`,
'X-Plex-Device-Screen-Resolution':