From 78444a9e643829823162389dee60cca70da56bff Mon Sep 17 00:00:00 2001 From: sct Date: Tue, 19 Jan 2021 15:10:15 +0000 Subject: [PATCH 1/4] fix(requests): update requests to approved when parent media is set as available fixes #688 --- server/entity/MediaRequest.ts | 45 +++++++++++++++++++++++----- server/subscriber/MediaSubscriber.ts | 31 ++++++++++++++++++- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index cf0903e8..3b64f472 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -151,6 +151,15 @@ export class MediaRequest { logger.error('No parent media!', { label: 'Media Request' }); return; } + + if (media[this.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE) { + logger.warn( + 'Media became available before request was approved. Approval notification will be skipped.', + { label: 'Media Request' } + ); + return; + } + const tmdb = new TheMovieDb(); if (this.media.mediaType === MediaType.MOVIE) { const movie = await tmdb.getMovie({ movieId: this.media.tmdbId }); @@ -205,7 +214,13 @@ export class MediaRequest { return; } const seasonRequestRepository = getRepository(SeasonRequest); - if (this.status === MediaRequestStatus.APPROVED) { + if ( + this.status === MediaRequestStatus.APPROVED && + // Do not update the status if the item is already partially available or available + media[this.is4k ? 'status4k' : 'status'] !== MediaStatus.AVAILABLE && + media[this.is4k ? 'status4k' : 'status'] !== + MediaStatus.PARTIALLY_AVAILABLE + ) { if (this.is4k) { media.status4k = MediaStatus.PROCESSING; } else { @@ -358,6 +373,21 @@ export class MediaRequest { }); const movie = await tmdb.getMovie({ movieId: this.media.tmdbId }); + const media = await mediaRepository.findOne({ + where: { id: this.media.id }, + }); + + if (!media) { + logger.error('Media not present'); + return; + } + + if ( + media[this.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE + ) { + throw new Error('Media already available'); + } + // Run this asynchronously so we don't wait for it on the UI side radarr .addMovie({ @@ -373,13 +403,6 @@ export class MediaRequest { }) .then(async (success) => { if (!success) { - const media = await mediaRepository.findOne({ - where: { id: this.media.id }, - }); - if (!media) { - logger.error('Media not present'); - return; - } media.status = MediaStatus.UNKNOWN; await mediaRepository.save(media); logger.warn( @@ -464,6 +487,12 @@ export class MediaRequest { throw new Error('Media data is missing'); } + if ( + media[this.is4k ? 'status4k' : 'status'] === MediaStatus.AVAILABLE + ) { + throw new Error('Media already available'); + } + const tmdb = new TheMovieDb(); const sonarr = new SonarrAPI({ apiKey: sonarrSettings.apiKey, diff --git a/server/subscriber/MediaSubscriber.ts b/server/subscriber/MediaSubscriber.ts index 92c4803e..6e5e285c 100644 --- a/server/subscriber/MediaSubscriber.ts +++ b/server/subscriber/MediaSubscriber.ts @@ -5,7 +5,7 @@ import { UpdateEvent, } from 'typeorm'; import TheMovieDb from '../api/themoviedb'; -import { MediaStatus, MediaType } from '../constants/media'; +import { MediaRequestStatus, MediaStatus, MediaType } from '../constants/media'; import Media from '../entity/Media'; import { MediaRequest } from '../entity/MediaRequest'; import Season from '../entity/Season'; @@ -102,6 +102,21 @@ export class MediaSubscriber implements EntitySubscriberInterface { } } + private async updateChildRequestStatus(event: Media, is4k: boolean) { + const requestRepository = getRepository(MediaRequest); + + const requests = await requestRepository.find({ + where: { media: event.id }, + }); + + for (const request of requests) { + if (request.is4k === is4k) { + request.status = MediaRequestStatus.APPROVED; + await requestRepository.save(request); + } + } + } + public beforeUpdate(event: UpdateEvent): void { if (!event.entity) { return; @@ -121,5 +136,19 @@ export class MediaSubscriber implements EntitySubscriberInterface { ) { this.notifyAvailableSeries(event.entity, event.databaseEntity); } + + if ( + event.entity.status === MediaStatus.AVAILABLE && + event.databaseEntity.status === MediaStatus.PENDING + ) { + this.updateChildRequestStatus(event.entity, false); + } + + if ( + event.entity.status4k === MediaStatus.AVAILABLE && + event.databaseEntity.status4k === MediaStatus.PENDING + ) { + this.updateChildRequestStatus(event.entity, true); + } } } From f7c0769deb997df58367cc60d0f0bbca781f2669 Mon Sep 17 00:00:00 2001 From: samwiseg0 <2241731+samwiseg0@users.noreply.github.com> Date: Tue, 19 Jan 2021 20:40:15 -0500 Subject: [PATCH 2/4] build(release): Add snap package to the release workflow [skip ci] (#692) * build(discord): Always send discord notification * build(snap): Remove trigger for tags * build(release): Add snap build to release workflow * Remove pull request if from discord snap --- .github/workflows/ci.yml | 1 + .github/workflows/release.yml | 53 +++++++++++++++++++++++++++++++++++ .github/workflows/snap.yaml | 4 +-- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 328044dc..cd2e0bcd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,7 @@ jobs: discord: name: Send Discord Notification needs: build_and_push + if: always() && github.event_name != 'pull_request' runs-on: ubuntu-20.04 steps: - name: Get Build Job Status diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 10158ab6..e8412f61 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,7 @@ on: jobs: test: + name: Lint & Test Build runs-on: ubuntu-20.04 container: node:12.18-alpine steps: @@ -41,9 +42,61 @@ jobs: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} run: npx semantic-release + build-snap: + name: Build Snap Package (${{ matrix.architecture }}) + needs: semantic-release + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + architecture: + - amd64 + - arm64 + - armhf + steps: + - name: Checkout Code + uses: actions/checkout@v2 + + - name: Prepare + id: prepare + run: | + git fetch --prune --unshallow --tags + if [[ $GITHUB_REF == refs/tags/* || $GITHUB_REF == refs/heads/master ]]; then + echo ::set-output name=RELEASE::stable + else + echo ::set-output name=RELEASE::edge + fi + + - name: Set Up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Build Snap Package + uses: diddlesnaps/snapcraft-multiarch-action@v1 + id: build + with: + architecture: ${{ matrix.architecture }} + + - name: Upload Snap Package + uses: actions/upload-artifact@v2 + with: + name: overseerr-snap-package-${{ matrix.architecture }} + path: ${{ steps.build.outputs.snap }} + + - name: Review Snap Package + uses: diddlesnaps/snapcraft-review-tools-action@v1 + with: + snap: ${{ steps.build.outputs.snap }} + + - name: Publish Snap Package + uses: snapcore/action-publish@v1 + with: + store_login: ${{ secrets.SNAP_LOGIN }} + snap: ${{ steps.build.outputs.snap }} + release: ${{ steps.prepare.outputs.RELEASE }} discord: name: Send Discord Notification needs: semantic-release + if: always() runs-on: ubuntu-20.04 steps: - name: Get Build Job Status diff --git a/.github/workflows/snap.yaml b/.github/workflows/snap.yaml index 4d415ae3..e0b100bf 100644 --- a/.github/workflows/snap.yaml +++ b/.github/workflows/snap.yaml @@ -3,8 +3,6 @@ name: Publish Snap on: push: branches: [develop] - tags: [v*] - pull_request: ~ jobs: test: @@ -74,10 +72,10 @@ jobs: store_login: ${{ secrets.SNAP_LOGIN }} snap: ${{ steps.build.outputs.snap }} release: ${{ steps.prepare.outputs.RELEASE }} - discord: name: Send Discord Notification needs: build-snap + if: always() runs-on: ubuntu-20.04 steps: - name: Get Build Job Status From e80920faa1dbc66434344132b55ab04e7119fa70 Mon Sep 17 00:00:00 2001 From: samwiseg0 <2241731+samwiseg0@users.noreply.github.com> Date: Tue, 19 Jan 2021 21:13:24 -0500 Subject: [PATCH 3/4] Add skip ci to discord notifications (#696) --- .github/workflows/ci.yml | 2 +- .github/workflows/snap.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd2e0bcd..142bb5c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: discord: name: Send Discord Notification needs: build_and_push - if: always() && github.event_name != 'pull_request' + if: always() && github.event_name != 'pull_request' && !contains(github.event.head_commit.message, '[skip ci]') runs-on: ubuntu-20.04 steps: - name: Get Build Job Status diff --git a/.github/workflows/snap.yaml b/.github/workflows/snap.yaml index e0b100bf..b7a2278a 100644 --- a/.github/workflows/snap.yaml +++ b/.github/workflows/snap.yaml @@ -75,7 +75,7 @@ jobs: discord: name: Send Discord Notification needs: build-snap - if: always() + if: always() && !contains(github.event.head_commit.message, '[skip ci]') runs-on: ubuntu-20.04 steps: - name: Get Build Job Status From e1032ff5dfac4a8c9d4da9cf2788c19822343ad9 Mon Sep 17 00:00:00 2001 From: sct Date: Wed, 20 Jan 2021 05:23:42 +0000 Subject: [PATCH 4/4] fix(requests): allow declined season requests to be re-requested fixes #690 --- server/routes/request.ts | 13 +++++++++++-- server/subscriber/MediaSubscriber.ts | 5 ++++- src/components/RequestModal/TvRequestModal.tsx | 13 ++++++++++--- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/server/routes/request.ts b/server/routes/request.ts index baa73616..0b726724 100644 --- a/server/routes/request.ts +++ b/server/routes/request.ts @@ -160,7 +160,11 @@ requestRoutes.post( // (Unless there are no seasons, in which case we abort) if (media.requests) { existingSeasons = media.requests - .filter((request) => request.is4k === req.body.is4k) + .filter( + (request) => + request.is4k === req.body.is4k && + request.status !== MediaRequestStatus.DECLINED + ) .reduce((seasons, request) => { const combinedSeasons = request.seasons.map( (season) => season.seasonNumber @@ -303,7 +307,12 @@ requestRoutes.put<{ requestId: string }>( // Get all requested seasons that are not part of this request we are editing const existingSeasons = media.requests - .filter((r) => r.is4k === request.is4k && r.id !== request.id) + .filter( + (r) => + r.is4k === request.is4k && + r.id !== request.id && + r.status !== MediaRequestStatus.DECLINED + ) .reduce((seasons, r) => { const combinedSeasons = r.seasons.map( (season) => season.seasonNumber diff --git a/server/subscriber/MediaSubscriber.ts b/server/subscriber/MediaSubscriber.ts index 6e5e285c..a1289fb0 100644 --- a/server/subscriber/MediaSubscriber.ts +++ b/server/subscriber/MediaSubscriber.ts @@ -110,7 +110,10 @@ export class MediaSubscriber implements EntitySubscriberInterface { }); for (const request of requests) { - if (request.is4k === is4k) { + if ( + request.is4k === is4k && + request.status === MediaRequestStatus.PENDING + ) { request.status = MediaRequestStatus.APPROVED; await requestRepository.save(request); } diff --git a/src/components/RequestModal/TvRequestModal.tsx b/src/components/RequestModal/TvRequestModal.tsx index 4ee0aa5a..f16dbb8e 100644 --- a/src/components/RequestModal/TvRequestModal.tsx +++ b/src/components/RequestModal/TvRequestModal.tsx @@ -169,7 +169,11 @@ const TvRequestModal: React.FC = ({ const getAllRequestedSeasons = (): number[] => { const requestedSeasons = (data?.mediaInfo?.requests ?? []) - .filter((request) => request.is4k === is4k) + .filter( + (request) => + request.is4k === is4k && + request.status !== MediaRequestStatus.DECLINED + ) .reduce((requestedSeasons, request) => { return [ ...requestedSeasons, @@ -255,8 +259,11 @@ const TvRequestModal: React.FC = ({ if ( data?.mediaInfo && - (data.mediaInfo.requests || []).filter((request) => request.is4k === is4k) - .length > 0 + (data.mediaInfo.requests || []).filter( + (request) => + request.is4k === is4k && + request.status !== MediaRequestStatus.DECLINED + ).length > 0 ) { data.mediaInfo.requests .filter((request) => request.is4k === is4k)