Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
node_modules
npm-debug.log
.vscode
src
# src
.env*
61 changes: 61 additions & 0 deletions .github/workflows/publish-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# .github/workflows/publish-docker.yml

name: Publish Docker Image

# Run this workflow on every push to your main branch
on:
push:
branches: [ "master" ] # Change to "main" if that's your default branch

jobs:
build-and-push:
# Use the latest version of Ubuntu to run the job
runs-on: ubuntu-latest
# Grant permissions for the job to write to the GitHub Package Registry
permissions:
contents: read
packages: write

steps:
# 1. Checks out your repository's code
- name: Checkout repository
uses: actions/checkout@v4

# 2. Sets up QEMU for multi-platform build emulation
- name: Set up QEMU
uses: docker/setup-qemu-action@v3

# 3. Sets up Docker Buildx, the builder that can create multi-platform images
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

# 4. Logs into the GitHub Container Registry using the secret you created
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GHCR_TOKEN }}

# 5. Extracts useful metadata like image tags
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
# tag with :latest for pushes to the default branch
type=raw,value=latest,enable={{is_default_branch}}
# tag with the branch name for other branches
type=ref,event=branch
# tag with the git sha
type=sha
# 6. Builds the image from your Dockerfile and pushes it to GHCR
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@ RUN npm install
# Bundle app source
COPY . .

# --- NEW DEBUGGING STEP ---
# List the files and directories to confirm they were copied correctly.
RUN find . -maxdepth 2

RUN npm run build

CMD [ "node", "dist/index.js" ]
2 changes: 1 addition & 1 deletion docker-compose-example.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "2.1"
services:
plex-requester-collections:
image: manybothans/plex-requester-collections:latest
image: ghcr.io/ahmaddxb/plex-requester-collections:latest
container_name: plex-requester-collections
environment:
- NODE_ENV=production # Optional - 'development' will give verbose logging.
Expand Down
7 changes: 4 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"main": "dist/index.js",
"scripts": {
"start": "tsc && node dist/index.js",
"build": "tsc",
"lint": "eslint . --ext .ts",
"test": "echo \"Error: no test specified\" && exit 1",
"dockerize": "tsc && docker build . -t manybothans/plex-requester-collections",
Expand Down
61 changes: 61 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const COLL_TITLE_PREFIX_SHOW = "TV Shows Requested by ";
const STALE_ADDED_DATE_THRESHOLD = moment().subtract(6, "months");
const STALE_VIEW_DATE_THRESHOLD = moment().subtract(3, "months");
const MS_24_HOURS = 86400000;
const TAG_PREFIX_WATCHED_BY = "watched_by:";

// Start after a delay, if set.
const startDelay =
Expand Down Expand Up @@ -325,6 +326,66 @@ const app = async function () {
order_dir: "desc"
});


// =========================================================================
// === FEATURE: Watched By Tagging (Controlled by FEATURE_WATCHED_BY flag)
// =========================================================================
if (process.env.FEATURE_WATCHED_BY === "1") {
console.log(" -> 'Watched By' feature is enabled.");

/************************************************************************************
* Clean up old watched_by tags before adding new ones.
************************************************************************************/
const allRadarrTags = await RadarrAPI.getTags();
if (allRadarrTags && radarrSonarrItem.tags.length > 0) {
const radarrTagMap = new Map(allRadarrTags.map(tag => [tag.id, tag.label]));
const viewerTagsToRemove = radarrSonarrItem.tags
.map(tagId => radarrTagMap.get(tagId))
.filter(tagName => tagName && tagName.startsWith(TAG_PREFIX_WATCHED_BY));

if (viewerTagsToRemove.length > 0) {
console.log(` -> Found ${viewerTagsToRemove.length} old watched_by tag(s) to remove.`);
for (const tagName of viewerTagsToRemove) {
console.log(` -> Removing old tag: ${tagName}`);
radarrSonarrItem = await RadarrAPI.removeTagFromMediaItem(
radarrSonarrItem.id,
tagName,
<RadarrMediaDetails>radarrSonarrItem
) || radarrSonarrItem;
}
}
}

/************************************************************************************
* Add tags for all users who have watched the movie.
************************************************************************************/
const uniqueViewers = _.uniqBy(
_.filter(
histories,
(session: TautulliHistoryDetails) => session && session.user
),
"user"
) as TautulliHistoryDetails[];

if (uniqueViewers && uniqueViewers.length > 0) {
console.log(` -> Found ${uniqueViewers.length} current unique viewer(s).`);
for (const viewer of uniqueViewers) {
const nameForTag = viewer.friendly_name || viewer.user;
const viewerTag = TAG_PREFIX_WATCHED_BY + nameForTag;
console.log(` -> Applying Radarr tag for viewer: ${nameForTag}`);
radarrSonarrItem = await RadarrAPI.addTagToMediaItem(
radarrSonarrItem?.id,
viewerTag,
<RadarrMediaDetails>radarrSonarrItem
) || radarrSonarrItem;
}
}
}
// =========================================================================
// === END of FEATURE: Watched By Tagging
// =========================================================================


// Filter history sessions to look at everyone expect the requester.
const filteredHistories_others = _.filter(
histories,
Expand Down