Skip to content

Commit 225c689

Browse files
committed
publish
1 parent c933f87 commit 225c689

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3370
-341
lines changed

.dockerignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
./config.yml
3+
.docker

.github/discord1.png

107 KB
Loading

.github/header.png

937 KB
Loading

.github/workflows/docker-publish.yml

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Docker Publish
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
tag:
7+
description: "Tag for the Docker image"
8+
required: true
9+
default: "latest"
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
permissions:
15+
contents: read
16+
packages: write
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v2
20+
21+
- name: Log in to GitHub Container Registry
22+
uses: docker/login-action@v2
23+
with:
24+
registry: ghcr.io
25+
username: ${{ github.actor }}
26+
password: ${{ secrets.GITHUB_TOKEN }}
27+
28+
- name: Build Docker image
29+
run: docker build -t ghcr.io/${{ github.repository }}:${{ github.event.inputs.tag }} .
30+
31+
- name: Push Docker image
32+
run: docker push ghcr.io/${{ github.repository }}:${{ github.event.inputs.tag }}

.gitignore

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
node_modules
22
dist
33
.docker
4-
config.yml
4+
/config.yml
5+
.env
6+
.env.test
7+
.DS_Store
8+
html

Dockerfile

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM node:20-alpine
2+
WORKDIR /app
3+
4+
RUN npm install -g pnpm
5+
COPY package.json pnpm-lock.yaml* ./
6+
RUN pnpm install --frozen-lockfile
7+
8+
COPY . .
9+
10+
ENV NODE_ENV production
11+
CMD ["pnpm", "start"]

README.md

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# byedarr
2+
3+
> Community driven media cleanup
4+
5+
![byedarr](.github/header.png)
6+
7+
## What is it?
8+
9+
**byedarr** is an application which cleans up your media library over time, letting your users vote on which items to keep or to remove.
10+
11+
## How does it work?
12+
13+
1. Connect to your media managers (sonarr/radarr)
14+
2. Connect to your Discord server
15+
3. The bot determines which items to remove, and posts messages to the selected channel:
16+
![byedarr](.github/discord1.png)
17+
4. After a set time the voting will conclude and the bot will either add the item to a whitelist, or flag it for deletion
18+
5. After the grace period the item will be removed
19+
20+
## Setup
21+
22+
1. Create a Discord bot and get the **bot** token: https://discord.com/developers/applications
23+
24+
2. Create a `.env` file with the following:
25+
26+
```
27+
DISCORD_BOT_TOKEN=<your token>
28+
```
29+
30+
3. Create a `config.yml` file
31+
32+
```yml
33+
mediaManagers:
34+
- id: "radarr"
35+
enabled: true
36+
type: "radarr"
37+
apiUrl: "http://radarr:7878/radarr"
38+
apiKey: "xxx"
39+
- id: "sonarr"
40+
enabled: true
41+
type: "sonarr"
42+
apiUrl: "http://sonarr:8989/sonarr"
43+
apiKey: "xxx"
44+
votingPeriod: "1 week"
45+
gracePeriod: "1 week"
46+
immunityPeriod: "3 months"
47+
discord:
48+
channelId: "123456" # Enable Discord developer mode and right-click the channel then press "Copy Channel ID"
49+
```
50+
51+
4. `docker-compose.yml`
52+
53+
```yml
54+
services:
55+
byedarr:
56+
image: ghcr.io/benjick/byedarr:latest
57+
env_file:
58+
- .env
59+
environment:
60+
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
61+
- CONFIG_PATH=/config/config.yml
62+
volumes:
63+
- ./config.yml:/config/config.yml
64+
restart: unless-stopped
65+
66+
db:
67+
image: postgres:16-alpine
68+
environment:
69+
- POSTGRES_USER=postgres
70+
- POSTGRES_PASSWORD=postgres
71+
- POSTGRES_DB=postgres
72+
volumes:
73+
- postgres_data:/var/lib/postgresql/data
74+
restart: unless-stopped
75+
76+
volumes:
77+
postgres_data:
78+
```
79+
80+
> ✋ The config is read on app start, so any changes will require a restart.
81+
82+
## Full docs
83+
84+
### Environment variables
85+
86+
| Variable | Type | Comment |
87+
| ----------------- | ------------ | --------------------------------------------------------- |
88+
| DISCORD_BOT_TOKEN | string | https://discord.com/developers/applications |
89+
| CONFIG_PATH | string | Path to the configuration file, e.g. `/config/config.yml` |
90+
| DATABASE_URL | string (URL) | PostgreSQL database connection URL |
91+
| DRY_RUN | boolean | If true, nothing is actually deleted from sonarr/radarr |
92+
| DEBUG | boolean | If true, debug messages are printed to the console |
93+
| SKIP_MIGRATIONS | boolean | If true, migrations are skipped |
94+
95+
### `config.yml`
96+
97+
```yaml
98+
cron:
99+
# How often to check for media to remove (default: daily at 6 PM)
100+
findMedia: "0 18 * * *"
101+
# How often to check for ended votes and process results (default: hourly)
102+
processVotes: "0 * * * *"
103+
104+
# List of media managers to use
105+
mediaManagers:
106+
- id: "unique_identifier" # Unique identifier for the media manager (don't change later)
107+
enabled: true # Should look for media in this manager
108+
apiUrl: "http://radarr:7878/radarr" # Base URL for the media manager's API (excluding '/api')
109+
apiKey: "xxx"
110+
type: "radarr" # Specifies the media manager software (radarr or sonarr)
111+
count: 1 # Number of media items to include in each voting round
112+
weights: # Weights for each media attribute
113+
age: 0.1 # Weight based on time since added to the media manager
114+
size: 0.3 # Weight based on media size
115+
rating: 1 # Weight based on media rating
116+
addImportExclusionOnDelete: false # If true, adds deleted items to import exclusion list
117+
118+
# Paths to ignore when processing media
119+
ignoredPaths:
120+
- "/path/to/ignore"
121+
- "/another/path"
122+
123+
# Discord settings
124+
discord:
125+
channelId: "discord_channel_id" # ID of the channel to send messages to
126+
127+
# Voting and grace period settings
128+
votingPeriod: "1 week" # How long a vote lasts
129+
gracePeriod: "2 weeks" # Wait time after vote ends before removing/whitelisting media
130+
immunityPeriod: "3 months" # Time a new media item is immune from voting
131+
132+
# What to do if a vote ends in a draw (keep or delete, default delete)
133+
onDraw: "delete" | "keep"
134+
135+
# If true, no media will actually be deleted from managers
136+
dryRun: false
137+
```

docker-compose.pull.yml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
services:
2+
app:
3+
image: ghcr.io/benjick/byedarr:latest
4+
env_file:
5+
- .env
6+
environment:
7+
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
8+
- CONFIG_PATH=/config/config.yml
9+
volumes:
10+
- ./config.yml:/config/config.yml
11+
restart: unless-stopped
12+
13+
db:
14+
image: postgres:16-alpine
15+
environment:
16+
- POSTGRES_USER=postgres
17+
- POSTGRES_PASSWORD=postgres
18+
- POSTGRES_DB=postgres
19+
volumes:
20+
- postgres_data:/var/lib/postgresql/data
21+
restart: unless-stopped
22+
23+
volumes:
24+
postgres_data:

docker-compose.test.yml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
services:
2+
app:
3+
build:
4+
context: .
5+
dockerfile: Dockerfile
6+
env_file:
7+
- .env
8+
environment:
9+
# - NODE_ENV=production
10+
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
11+
- CONFIG_PATH=/config/config.yml
12+
- DEBUG=true
13+
volumes:
14+
- ./config.yml:/config/config.yml
15+
restart: unless-stopped
16+
17+
db:
18+
image: postgres:16-alpine
19+
environment:
20+
- POSTGRES_USER=postgres
21+
- POSTGRES_PASSWORD=postgres
22+
- POSTGRES_DB=postgres
23+
volumes:
24+
- postgres_data:/var/lib/postgresql/data
25+
restart: unless-stopped
26+
27+
volumes:
28+
postgres_data:

drizzle.config.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import { type Config } from "drizzle-kit";
22

3-
const connectionString =
4-
process.env.DATABASE_URL ?? "postgres://postgres:postgres@localhost:5432/postgres";
5-
6-
console.log("connectionString", connectionString);
3+
import { env } from "./src/env";
74

85
export default {
96
schema: ["./src/db/schema/*.ts"],
107
dialect: "postgresql",
118
dbCredentials: {
12-
url: connectionString,
9+
url: env.DATABASE_URL,
1310
},
1411
out: "./src/db/migrations",
1512
} satisfies Config;

package.json

+16-6
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,49 @@
22
"name": "byedarr",
33
"version": "1.0.0",
44
"description": "byedarr - a media manager helper",
5-
"main": "index.js",
65
"scripts": {
6+
"start": "tsx src/index.ts",
77
"dev": "NODE_ENV=development tsx watch src/index.ts",
88
"dev:db": "docker compose -f docker-compose.dev.yml up",
9-
"test": "echo \"Error: no test specified\" && exit 1",
9+
"test:docker": "docker compose -f docker-compose.test.yml -p byedarr-test up",
10+
"test:docker:down": "docker compose -f docker-compose.test.yml -p byedarr-test down -v",
11+
"pull:docker": "docker compose -f docker-compose.pull.yml -p byedarr-pull up",
12+
"pull:docker:down": "docker compose -f docker-compose.pull.yml -p byedarr-pull down -v",
13+
"test": "vitest",
1014
"db:push": "dotenv drizzle-kit push",
1115
"db:migrate": "dotenv drizzle-kit migrate",
1216
"db:up": "dotenv drizzle-kit up",
1317
"db:generate-migrations": "dotenv drizzle-kit generate",
1418
"db:validate-migrations": "pnpm db:generate-migrations | tee migrations.log | grep -q \"nothing to migrate\" || exit 1",
1519
"db:studio": "dotenv drizzle-kit studio",
16-
"openapi:radarr": "npx openapi-typescript https://raw.githubusercontent.com/Radarr/Radarr/develop/src/Radarr.Api.V3/openapi.json -o ./src/lib/radarr/generated.ts",
17-
"openapi:sonarr": "npx openapi-typescript https://raw.githubusercontent.com/Sonarr/Sonarr/develop/src/Sonarr.Api.V3/openapi.json -o ./src/lib/sonarr/generated.ts"
20+
"openapi:radarr": "npx openapi-typescript https://raw.githubusercontent.com/Radarr/Radarr/develop/src/Radarr.Api.V3/openapi.json -o ./src/lib/managers/radarr/generated.ts",
21+
"openapi:sonarr": "npx openapi-typescript https://raw.githubusercontent.com/Sonarr/Sonarr/develop/src/Sonarr.Api.V3/openapi.json -o ./src/lib/managers/sonarr/generated.ts"
1822
},
1923
"keywords": [],
2024
"author": "",
2125
"license": "ISC",
2226
"dependencies": {
2327
"@types/node-cron": "^3.0.11",
28+
"dayjs": "^1.11.13",
29+
"discord.js": "^14.16.2",
30+
"dotenv": "^16.4.5",
2431
"drizzle-kit": "^0.24.2",
2532
"drizzle-orm": "^0.33.0",
2633
"drizzle-zod": "^0.5.1",
2734
"js-yaml": "^4.1.0",
2835
"node-cron": "^3.0.3",
2936
"openapi-fetch": "^0.12.0",
30-
"pg": "^8.12.0",
37+
"postgres": "^3.4.4",
3138
"pretty-bytes": "^6.1.1",
3239
"typescript": "^5.6.2",
3340
"zod": "^3.23.8"
3441
},
3542
"devDependencies": {
3643
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
44+
"@types/js-yaml": "^4.0.9",
45+
"@vitest/ui": "^2.1.1",
3746
"dotenv-cli": "^7.4.2",
38-
"tsx": "^4.19.1"
47+
"tsx": "^4.19.1",
48+
"vitest": "^2.1.1"
3949
}
4050
}

0 commit comments

Comments
 (0)