Skip to content
This repository was archived by the owner on Jul 14, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8b465dc
fix: correct edge case with deletion not updating requests (#4110)
OwsleyJr Apr 15, 2025
9275a09
fix: correct notification sending for wrong status (#4113)
OwsleyJr Apr 16, 2025
60a1830
fix(notifications): sending test-notifications for email now respects…
vfaergestad Apr 17, 2025
d543eaa
docs: add vfaergestad as a contributor for code (#4114)
allcontributors[bot] Apr 17, 2025
064ae92
fix: handle currently available non-completed media (#4115)
OwsleyJr Apr 18, 2025
8cd53cd
fix: handle partial seasons more reliably (#4116)
OwsleyJr Apr 18, 2025
208d311
fix: pwa app badge (#4117)
OwsleyJr Apr 19, 2025
7f868f3
fix: filter specials from modal all seasons and watchlist (#4108)
OwsleyJr Apr 20, 2025
f3cc3da
fix: bypass specials when partial requests is disabled (#4126)
OwsleyJr May 3, 2025
df4315a
refactor(MediaRequest): consolidate lifecycle hooks into subscriber &…
fallenbagel May 5, 2025
82bef4e
fix(mediaentity): change cascade from `update` to `remove` (#4135)
fallenbagel May 9, 2025
07dc8d7
fix(mediasubscriber): remove async from afterInsert and afterUpdate (…
gauthier-th May 9, 2025
21b188b
fix: better handling for active webpush subscription (#4146)
OwsleyJr Jun 12, 2025
a1d3977
fix: add push subscription verification and resubscribe logic (#4163)
OwsleyJr Jun 27, 2025
9bd4d33
fix: correct width of active subscription button (#4168)
OwsleyJr Jun 27, 2025
325eecb
fix: add flag in local storage for notifications (#4180)
OwsleyJr Jul 3, 2025
b1c6e2f
fix(api): update Plex Watchlist URL (#4220)
0xSysR3ll Aug 14, 2025
5ef098f
docs: add 0xSysR3ll as a contributor for code (#4221) [skip ci]
allcontributors[bot] Aug 14, 2025
a47359c
Merge upstream sct/overseerr develop branch to sync fork
claude Nov 12, 2025
dddb591
docs: add PR description for upstream sync
claude Nov 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,24 @@
"contributions": [
"code"
]
},
{
"login": "vfaergestad",
"name": "vfaergestad",
"avatar_url": "https://avatars.githubusercontent.com/u/49147564?v=4",
"profile": "https://github.com/vfaergestad",
"contributions": [
"code"
]
},
{
"login": "0xSysR3ll",
"name": "0xsysr3ll",
"avatar_url": "https://avatars.githubusercontent.com/u/31414959?v=4",
"profile": "https://github.com/0xSysR3ll",
"contributions": [
"code"
]
}
],
"badgeTemplate": "<a href=\"#contributors-\"><img alt=\"All Contributors\" src=\"https://img.shields.io/badge/all_contributors-<%= contributors.length %>-orange.svg\"/></a>",
Expand Down
70 changes: 70 additions & 0 deletions PR_UPSTREAM_SYNC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Sync Fork with Upstream Overseerr (18 Bug Fixes & Improvements)

This PR merges the latest changes from `sct/overseerr` develop branch to bring our Requesterr fork up to date with upstream improvements.

## πŸ“¦ What's Included

Merges **18 commits** from upstream Overseerr (commits `4cc3495..5ef098f`):

### πŸ”§ Major Fixes

#### Plex Integration
- fix(api): update Plex Watchlist URL (#4220)

#### Web Push Notifications (Multiple improvements)
- fix: add push subscription verification and resubscribe logic (#4163)
- fix: better handling for active webpush subscription (#4146)
- fix: add flag in local storage for notifications (#4180)
- fix: correct width of active subscription button (#4168)
- New utility: `pushSubscriptionHelpers.ts`

#### Media Request System
- refactor(MediaRequest): consolidate lifecycle hooks into subscriber & restrict cascade to prevent recursion (#4124)
- fix(mediaentity): change cascade from `update` to `remove` (#4135)
- fix(mediasubscriber): remove async from afterInsert and afterUpdate (#4136)
- fix: correct edge case with deletion not updating requests (#4110)

#### TV Show / Season Handling
- fix: handle partial seasons more reliably (#4116)
- fix: bypass specials when partial requests is disabled (#4126)
- fix: filter specials from modal all seasons and watchlist (#4108)

#### Other Improvements
- fix: handle currently available non-completed media (#4115)
- fix: correct notification sending for wrong status (#4113)
- fix(notifications): email test notifications now respect allowSelfSigned value (#4112)
- fix: pwa app badge (#4117)

#### Contributors
- Added 0xSysR3ll and vfaergestad as contributors

## 🎨 Requesterr Branding Preserved

All Requesterr customizations remain intact:
- βœ… Translation files (all 30 languages with "Requesterr" branding)
- βœ… CSS classes (`.text-requesterr`)
- βœ… Default settings values
- βœ… README.md fork attribution and branding

## πŸ”€ Merge Conflicts Resolved

**Only one conflict:** `README.md` header section
- **Resolution:** Kept Requesterr branding with proper upstream attribution
- All other files merged cleanly without conflicts

## πŸ“Š Files Changed

- **19 modified files**
- **1 new file** (`src/utils/pushSubscriptionHelpers.ts`)

## βœ… Testing Notes

All changes are from the stable Overseerr develop branch and have been tested upstream. The merge:
- Preserves all Requesterr-specific modifications
- Brings critical bug fixes for media requests and notifications
- Improves stability for Plex integration and season handling

## πŸ”— Related Links

- Upstream repository: https://github.com/sct/overseerr
- Upstream commits: https://github.com/sct/overseerr/compare/4cc3495...5ef098f
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="http://indicus.ro"><img src="https://avatars.githubusercontent.com/u/1199404?v=4?s=100" width="100px;" alt="Stancu Florin"/><br /><sub><b>Stancu Florin</b></sub></a><br /><a href="https://github.com/sct/overseerr/commits?author=StancuFlorin" title="Code">πŸ’»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lmiklosko"><img src="https://avatars.githubusercontent.com/u/44380311?v=4?s=100" width="100px;" alt="Lukas Miklosko"/><br /><sub><b>Lukas Miklosko</b></sub></a><br /><a href="https://github.com/sct/overseerr/commits?author=lmiklosko" title="Code">πŸ’»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://gauthierth.fr/"><img src="https://avatars.githubusercontent.com/u/37781713?v=4?s=100" width="100px;" alt="Gauthier"/><br /><sub><b>Gauthier</b></sub></a><br /><a href="https://github.com/sct/overseerr/commits?author=gauthier-th" title="Code">πŸ’»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vfaergestad"><img src="https://avatars.githubusercontent.com/u/49147564?v=4?s=100" width="100px;" alt="vfaergestad"/><br /><sub><b>vfaergestad</b></sub></a><br /><a href="https://github.com/sct/overseerr/commits?author=vfaergestad" title="Code">πŸ’»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/0xSysR3ll"><img src="https://avatars.githubusercontent.com/u/31414959?v=4?s=100" width="100px;" alt="0xsysr3ll"/><br /><sub><b>0xsysr3ll</b></sub></a><br /><a href="https://github.com/sct/overseerr/commits?author=0xSysR3ll" title="Code">πŸ’»</a></td>
</tr>
</tbody>
</table>
Expand Down
6 changes: 3 additions & 3 deletions overseerr-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3617,7 +3617,7 @@ paths:
type: string
userAgent:
type: string
/user/{userId}/pushSubscription/{key}:
/user/{userId}/pushSubscription/{endpoint}:
get:
summary: Get web push notification settings for a user
description: |
Expand All @@ -3631,7 +3631,7 @@ paths:
schema:
type: number
- in: path
name: key
name: endpoint
required: true
schema:
type: string
Expand Down Expand Up @@ -3663,7 +3663,7 @@ paths:
schema:
type: number
- in: path
name: key
name: endpoint
required: true
schema:
type: string
Expand Down
4 changes: 2 additions & 2 deletions server/api/plextv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ class PlexTvAPI extends ExternalAPI {
headers: {
'If-None-Match': cachedWatchlist?.etag,
},
baseURL: 'https://metadata.provider.plex.tv',
baseURL: 'https://discover.provider.plex.tv',
validateStatus: (status) => status < 400, // Allow HTTP 304 to return without error
}
);
Expand All @@ -315,7 +315,7 @@ class PlexTvAPI extends ExternalAPI {
const detailedResponse = await this.getRolling<MetadataResponse>(
`/library/metadata/${watchlistItem.ratingKey}`,
{
baseURL: 'https://metadata.provider.plex.tv',
baseURL: 'https://discover.provider.plex.tv',
}
);

Expand Down
117 changes: 4 additions & 113 deletions server/entity/Media.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import RadarrAPI from '@server/api/servarr/radarr';
import SonarrAPI from '@server/api/servarr/sonarr';
import {
MediaRequestStatus,
MediaStatus,
MediaType,
} from '@server/constants/media';
import { MediaStatus, MediaType } from '@server/constants/media';
import { getRepository } from '@server/datasource';
import SeasonRequest from '@server/entity/SeasonRequest';
import type { DownloadingItem } from '@server/lib/downloadtracker';
import downloadTracker from '@server/lib/downloadtracker';
import { getSettings } from '@server/lib/settings';
import logger from '@server/logger';
import {
AfterLoad,
AfterUpdate,
Column,
CreateDateColumn,
Entity,
Expand Down Expand Up @@ -96,7 +90,9 @@ class Media {
@Column({ type: 'int', default: MediaStatus.UNKNOWN })
public status4k: MediaStatus;

@OneToMany(() => MediaRequest, (request) => request.media, { cascade: true })
@OneToMany(() => MediaRequest, (request) => request.media, {
cascade: ['insert', 'remove'],
})
public requests: MediaRequest[];

@OneToMany(() => Season, (season) => season.media, {
Expand Down Expand Up @@ -315,111 +311,6 @@ class Media {
}
}
}

@AfterUpdate()
public async updateRelatedMediaRequest(): Promise<void> {
const requestRepository = getRepository(MediaRequest);
const seasonRequestRepository = getRepository(SeasonRequest);

const validStatuses = [
MediaStatus.PARTIALLY_AVAILABLE,
MediaStatus.AVAILABLE,
MediaStatus.DELETED,
];

if (
validStatuses.includes(this.status) ||
validStatuses.includes(this.status4k)
) {
const relatedRequests = await requestRepository.find({
relations: {
media: true,
},
where: {
media: { id: this.id },
status: MediaRequestStatus.APPROVED,
},
});

// Check the media entity status and if available
// or deleted, set the related request to completed
if (relatedRequests.length > 0) {
const completedRequests: MediaRequest[] = [];

relatedRequests.forEach((request) => {
let shouldComplete = false;

if (
this[request.is4k ? 'status4k' : 'status'] ===
MediaStatus.AVAILABLE ||
this[request.is4k ? 'status4k' : 'status'] === MediaStatus.DELETED
) {
shouldComplete = true;
} else if (this.mediaType === 'tv') {
// For TV, check if all requested seasons are available or deleted
const allSeasonsReady = request.seasons.every((requestSeason) => {
const matchingSeason = this.seasons.find(
(mediaSeason) =>
mediaSeason.seasonNumber === requestSeason.seasonNumber
);

if (!matchingSeason) {
return false;
}

return (
matchingSeason[request.is4k ? 'status4k' : 'status'] ===
MediaStatus.AVAILABLE ||
matchingSeason[request.is4k ? 'status4k' : 'status'] ===
MediaStatus.DELETED
);
});

shouldComplete = allSeasonsReady;
}

if (shouldComplete) {
request.status = MediaRequestStatus.COMPLETED;
completedRequests.push(request);
}
});

await requestRepository.save(completedRequests);

// Handle season requests and mark them completed when
// that specific season becomes available
if (this.mediaType === 'tv') {
const seasonsToUpdate = relatedRequests.flatMap((request) => {
return request.seasons.filter((requestSeason) => {
const matchingSeason = this.seasons.find(
(mediaSeason) =>
mediaSeason.seasonNumber === requestSeason.seasonNumber
);

if (!matchingSeason) {
return false;
}

return (
matchingSeason[request.is4k ? 'status4k' : 'status'] ===
MediaStatus.AVAILABLE ||
matchingSeason[request.is4k ? 'status4k' : 'status'] ===
MediaStatus.DELETED
);
});
});

await Promise.all(
seasonsToUpdate.map((season) =>
seasonRequestRepository.update(season.id, {
status: MediaRequestStatus.COMPLETED,
})
)
);
}
}
}
}
}

export default Media;
Loading
Loading