├── docs ├── static │ ├── .nojekyll │ ├── robots.txt │ └── img │ │ ├── logo.webp │ │ ├── favicon.ico │ │ ├── trash_logo.webp │ │ └── experiment.svg ├── docs │ ├── installation │ │ ├── _images │ │ │ └── unraid_setup.webp │ │ ├── _category_.json │ │ ├── _include │ │ │ └── docker-basic-conf.yml │ │ └── binary.md │ ├── configuration │ │ ├── _category_.json │ │ ├── _include │ │ │ └── unmanaged-customformats.yml │ │ ├── scheduled.md │ │ ├── environment-variables.md │ │ ├── general.md │ │ └── telemetry.md │ ├── changelog.mdx │ ├── concepts.md │ └── faq.md ├── src │ ├── pages │ │ ├── markdown-page.md │ │ ├── index.module.css │ │ └── index.tsx │ ├── components │ │ └── HomepageFeatures │ │ │ ├── styles.module.css │ │ │ └── index.tsx │ └── css │ │ └── custom.css ├── tsconfig.json ├── .gitignore ├── README.md ├── sidebars.ts └── package.json ├── src ├── types │ ├── arr.types.ts │ ├── helper.types.ts │ ├── recyclarr.types.ts │ ├── download-client.types.ts │ ├── common.types.ts │ └── trashguide.types.ts ├── holidays.ts ├── downloadClientConfig │ └── downloadClientConfig.types.ts ├── metadataProfiles │ ├── metadataProfileBase.test.ts │ ├── metadataProfile.types.ts │ └── metadataProfileSyncer.ts ├── rootFolder │ ├── rootFolder.types.ts │ ├── rootFolderSyncer.ts │ └── rootFolderBase.test.ts ├── tags.ts ├── downloadClients │ └── downloadClientSyncer.ts ├── __generated__ │ ├── lidarr │ │ ├── Logout.ts │ │ ├── Path.ts │ │ ├── Content.ts │ │ ├── Ping.ts │ │ ├── Feed.ts │ │ └── Login.ts │ ├── radarr │ │ ├── Logout.ts │ │ ├── Path.ts │ │ ├── Content.ts │ │ ├── Ping.ts │ │ ├── Feed.ts │ │ └── Login.ts │ ├── sonarr │ │ ├── Logout.ts │ │ ├── Path.ts │ │ ├── Content.ts │ │ ├── Ping.ts │ │ ├── Feed.ts │ │ └── Login.ts │ ├── readarr │ │ ├── Logout.ts │ │ ├── Path.ts │ │ ├── Content.ts │ │ ├── Ping.ts │ │ ├── Feed.ts │ │ └── Login.ts │ ├── whisparr │ │ ├── Logout.ts │ │ ├── Path.ts │ │ ├── Content.ts │ │ ├── Ping.ts │ │ ├── Feed.ts │ │ └── Login.ts │ └── mergedTypes.ts ├── logger.ts ├── media-management.ts ├── local-importer.ts ├── url-template-importer.ts ├── recyclarr-importer.ts ├── cache.ts ├── env.ts ├── quality-definitions.test.ts └── quality-definitions.ts ├── CNAME ├── .prettierignore ├── examples ├── scheduled │ ├── .gitignore │ ├── config │ │ ├── secrets.yml │ │ └── config.yml │ ├── cron │ │ ├── configarr-reuse │ │ └── configarr-run │ ├── docker-compose.yml │ ├── radarr.xml │ ├── ofelia.ini │ ├── docker-compose.ofelia.yml │ ├── docker-compose.cron.yml │ └── README.md └── full │ ├── .gitignore │ ├── config │ └── secrets.yml │ ├── templates │ ├── lidarr.yml │ ├── readarr.yml │ ├── sonarr-quality.yml │ ├── radarr-quality.yml │ ├── whisparr.yml │ ├── sonarr-cf.yml │ └── radarr-cf.yml │ ├── docker-compose.jobs.yml │ ├── lidarr.xml │ ├── radarr.xml │ ├── sonarr.xml │ ├── cfs │ └── custom-size-bigger-40gb.json │ ├── readarr.xml │ ├── whisparr.xml │ ├── docker-compose.local.yml │ ├── README.md │ └── docker-compose.yml ├── secrets.yml.template ├── .vscode ├── settings.json └── launch.json ├── .prettierrc.js ├── pkgs └── nix │ ├── module │ ├── default.nix │ ├── options.nix │ └── config.nix │ └── package.nix ├── .claude └── settings.local.json ├── vitest.config.ts ├── .dockerignore ├── tests └── samples │ ├── quality_without_grouping.json │ ├── quality_with_grouping.json │ ├── single_custom_format.json │ └── 20240930_cf_exceptLanguage.json ├── .env.template ├── esbuild.ts ├── custom └── cfs │ ├── custom-size-bigger-40gb.json │ ├── lang-de-dl.json │ ├── lang-mic-dub.json │ ├── lang-not-en-de.json │ ├── lang-de-dl-2.json │ ├── lang-de-only.json │ ├── lang-en-only.json │ ├── de-tier-03.json │ └── de-tier-01.json ├── .github ├── workflows │ ├── lint.yml │ ├── release-it.yml │ ├── doc-preview.yml │ └── documentation.yml └── renovate.json5 ├── tsconfig.json ├── flake.nix ├── .release-it.json ├── DEV.md ├── config.yml.template ├── package.json ├── Dockerfile ├── GEMINI.md ├── Dockerfile-deno.Dockerfile ├── flake.lock ├── playwright.config.ts ├── generate-api.ts └── .gitignore /docs/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/types/arr.types.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | configarr.raydak.de 2 | configarr.de 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml 2 | docs/build 3 | -------------------------------------------------------------------------------- /examples/scheduled/.gitignore: -------------------------------------------------------------------------------- 1 | !config/*.yml 2 | dockerrepos/ 3 | -------------------------------------------------------------------------------- /secrets.yml.template: -------------------------------------------------------------------------------- 1 | SONARR_API_KEY: replace-with-sonarr-api-key 2 | -------------------------------------------------------------------------------- /examples/full/.gitignore: -------------------------------------------------------------------------------- 1 | !config/*.yml 2 | dockerrepos/ 3 | data/ 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "yaml.customTags": ["!secret", "!env", "!file"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/scheduled/config/secrets.yml: -------------------------------------------------------------------------------- 1 | RADARR_API_KEY: 0daa3a2b940f4e08bac991e9a30e9e12 2 | -------------------------------------------------------------------------------- /docs/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | Sitemap: https://configarr.de/sitemap.xml 4 | -------------------------------------------------------------------------------- /docs/static/img/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raydak-labs/configarr/HEAD/docs/static/img/logo.webp -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import("prettier").Options} */ 2 | export default { 3 | printWidth: 140, 4 | }; 5 | -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raydak-labs/configarr/HEAD/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /pkgs/nix/module/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | imports = [ 3 | ./config.nix 4 | ./options.nix 5 | ]; 6 | } 7 | -------------------------------------------------------------------------------- /docs/static/img/trash_logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raydak-labs/configarr/HEAD/docs/static/img/trash_logo.webp -------------------------------------------------------------------------------- /.claude/settings.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "allow": ["Bash(pnpm test:*)"], 4 | "deny": [], 5 | "ask": [] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /docs/docs/installation/_images/unraid_setup.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raydak-labs/configarr/HEAD/docs/docs/installation/_images/unraid_setup.webp -------------------------------------------------------------------------------- /docs/docs/configuration/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Configuration", 3 | "position": 4, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ["src/**/*.test.ts"], 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | .gitignore 4 | *.md 5 | dist 6 | repos 7 | dockerrepos 8 | playwright-report 9 | test-results 10 | config.yml 11 | secrets.yml 12 | -------------------------------------------------------------------------------- /src/holidays.ts: -------------------------------------------------------------------------------- 1 | import { logger } from "./logger"; 2 | 3 | export const logHolidayGreeting = (): void => { 4 | logger.info("🎄 Happy Christmas holidays and a Happy New Year! 🎉"); 5 | }; 6 | -------------------------------------------------------------------------------- /docs/docs/installation/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Installation", 3 | "position": 3, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Learn how to install Configarr" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/samples/quality_without_grouping.json: -------------------------------------------------------------------------------- 1 | { 2 | "quality": { 3 | "id": 5, 4 | "name": "WEBDL-720p", 5 | "source": "web", 6 | "resolution": 720 7 | }, 8 | "items": [], 9 | "allowed": true 10 | } 11 | -------------------------------------------------------------------------------- /examples/scheduled/cron/configarr-reuse: -------------------------------------------------------------------------------- 1 | LOGFILE_DEFAULT=/var/log/cron.log 2 | #CONFIGARR_FULL_PATH=/tmp/configarr/full/path 3 | 4 | * * * * * root docker compose -f /app/docker-compose.yaml -p cron start configarr >> $LOGFILE_DEFAULT 2>&1 5 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /examples/scheduled/cron/configarr-run: -------------------------------------------------------------------------------- 1 | LOGFILE_DEFAULT=/var/log/cron.log 2 | #CONFIGARR_FULL_PATH=/tmp/configarr/full/path 3 | 4 | * * * * * root docker compose -f /app/docker-compose.yaml -p cron run --rm configarr-run >> $LOGFILE_DEFAULT 2>&1 5 | -------------------------------------------------------------------------------- /examples/full/config/secrets.yml: -------------------------------------------------------------------------------- 1 | SONARR_API_KEY: 5e792b7e0fe14f58b8e92bf0902d4a44 2 | RADARR_API_KEY: 0daa3a2b940f4e08bac991e9a30e9e12 3 | WHISPARR_API_KEY: 2ebd18db81e14c2d98d06ef4b865aaa8 4 | READARR_API_KEY: e001718ba38a424d8aae7cb391b5d27a 5 | LIDARR_API_KEY: 713067b7319045df9c7fa6847d8ce717 6 | -------------------------------------------------------------------------------- /src/downloadClientConfig/downloadClientConfig.types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Download Client Configuration sync types 3 | * Handles instance-specific configuration for download clients 4 | */ 5 | 6 | export type DownloadClientConfigSyncResult = { 7 | updated: boolean; 8 | arrType: string; 9 | }; 10 | -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | #ROOT_PATH=/app 2 | #CUSTOM_REPO_ROOT=/app/repos 3 | #CONFIG_LOCATION=/app/config/config.yml 4 | #SECRETS_LOCATION=/app/config/secrets.yml 5 | #DRY_RUN=true # not fully supported yet 6 | #LOAD_LOCAL_SAMPLES=false 7 | #DEBUG_CREATE_FILES=false 8 | #LOG_LEVEL=info 9 | #TELEMETRY_ENABLED=false 10 | -------------------------------------------------------------------------------- /src/types/helper.types.ts: -------------------------------------------------------------------------------- 1 | // The default utility type is not typed in the keys 2 | type OmitTyped = Omit; 3 | 4 | type OmitTyped2 = { [P in Exclude]: T[P] }; 5 | 6 | type Subset = T; 7 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/docs/changelog.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 8 3 | description: "Changelog / Version changes" 4 | keywords: [changelog, version] 5 | title: Changelog 6 | --- 7 | 8 | import CodeBlock from "@theme/CodeBlock"; 9 | import { default as CHANGELOG } from "../../CHANGELOG.md"; 10 | 11 | # 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/full/templates/lidarr.yml: -------------------------------------------------------------------------------- 1 | quality_profiles: 2 | - name: ExampleProfile 3 | reset_unmatched_scores: 4 | enabled: true 5 | upgrade: 6 | allowed: true 7 | until_quality: FLAC 8 | until_score: 10 9 | min_format_score: 1 10 | quality_sort: top 11 | qualities: 12 | - name: FLAC 13 | - name: "MP3-96" 14 | -------------------------------------------------------------------------------- /examples/full/templates/readarr.yml: -------------------------------------------------------------------------------- 1 | quality_profiles: 2 | - name: ExampleProfile 3 | reset_unmatched_scores: 4 | enabled: true 5 | upgrade: 6 | allowed: true 7 | until_quality: MOBI 8 | until_score: 1000 9 | min_format_score: 5 10 | quality_sort: top 11 | qualities: 12 | - name: EPUB 13 | - name: MOBI 14 | -------------------------------------------------------------------------------- /examples/full/docker-compose.jobs.yml: -------------------------------------------------------------------------------- 1 | services: 2 | configarr: 3 | image: ghcr.io/raydak-labs/configarr:latest 4 | networks: 5 | - configarr-full 6 | volumes: 7 | - ./config:/app/config 8 | - ./dockerrepos:/app/repos 9 | - ./cfs:/app/cfs 10 | - ./templates:/app/templates 11 | 12 | networks: 13 | configarr-full: 14 | name: configarr-full 15 | external: true 16 | -------------------------------------------------------------------------------- /src/metadataProfiles/metadataProfileBase.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from "vitest"; 2 | 3 | describe("BaseMetadataProfileSync", () => { 4 | it("should be an abstract class that requires implementation", () => { 5 | // This is an abstract class - concrete implementations are tested separately 6 | // in metadataProfileLidarr.test.ts and metadataProfileReadarr.test.ts 7 | expect(true).toBe(true); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /examples/scheduled/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | radarr: 3 | image: lscr.io/linuxserver/radarr:6.0.4 4 | networks: 5 | - configarr-full 6 | environment: 7 | - PUID=1000 8 | - PGID=1000 9 | - TZ=Etc/UTC 10 | volumes: 11 | - radarr:/config 12 | - ${PWD}/radarr.xml:/config/config.xml:rw 13 | ports: 14 | - 6501:7878 15 | restart: unless-stopped 16 | 17 | networks: 18 | configarr-full: 19 | name: configarr-full 20 | 21 | volumes: 22 | radarr: 23 | -------------------------------------------------------------------------------- /esbuild.ts: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | 3 | const externalizedModules = {}; 4 | 5 | await esbuild.build({ 6 | //inject: ["cjs-shim.ts"], 7 | entryPoints: ["./src/index.ts"], 8 | bundle: true, 9 | sourcemap: "inline", 10 | platform: "node", 11 | target: "node20", 12 | //external: ["fs", "child_process", "crypto", "os", "path"], 13 | //plugins: [externalNativeModulesPlugin(externalizedModules)], 14 | 15 | format: "cjs", 16 | outfile: "bundle.cjs", 17 | 18 | //format: "esm", 19 | //outfile: "out2.mjs", 20 | }); 21 | -------------------------------------------------------------------------------- /examples/full/templates/sonarr-quality.yml: -------------------------------------------------------------------------------- 1 | quality_profiles: 2 | - name: ExampleProfile 3 | reset_unmatched_scores: 4 | enabled: true 5 | upgrade: 6 | allowed: true 7 | until_quality: WEB 2160p 8 | until_score: 1000 9 | min_format_score: 0 10 | quality_sort: top 11 | qualities: 12 | - name: WEB 2160p 13 | qualities: 14 | - WEBDL-2160p 15 | - WEBRip-2160p 16 | - name: WEB 1080p 17 | qualities: 18 | - WEBDL-1080p 19 | - WEBRip-1080p 20 | -------------------------------------------------------------------------------- /custom/cfs/custom-size-bigger-40gb.json: -------------------------------------------------------------------------------- 1 | { 2 | "trash_id": "custom-size-more-40gb", 3 | "trash_scores": { 4 | "default": -10000 5 | }, 6 | "trash_description": "Size: Block sizes over 40GB", 7 | "name": "Size: Block More 40GB", 8 | "includeCustomFormatWhenRenaming": false, 9 | "specifications": [ 10 | { 11 | "name": "Size", 12 | "implementation": "SizeSpecification", 13 | "negate": true, 14 | "required": true, 15 | "fields": { 16 | "min": 0, 17 | "max": 40 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /docs/docs/configuration/_include/unmanaged-customformats.yml: -------------------------------------------------------------------------------- 1 | sonarr: 2 | instance1: 3 | base_url: http://sonarr:8989 4 | api_key: !secret SONARR_API_KEY 5 | 6 | # since v1.13.0. You can assign custom formats to quality profiles which are only managed on the server 7 | custom_formats: 8 | - trash_ids: 9 | - 47435ece6b99a0b477caf360e79ba0bb # x265 (HD) 10 | assign_scores_to: 11 | - name: Any 12 | 13 | # 'Any' already exists on server 14 | # quality_profiles: 15 | # - name: Any 16 | # # ... 17 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | gap: 2rem; 24 | } 25 | 26 | .buttonsGithubStars { 27 | display: flex; 28 | } 29 | -------------------------------------------------------------------------------- /custom/cfs/lang-de-dl.json: -------------------------------------------------------------------------------- 1 | { 2 | "trash_id": "lang-de-dl-version1", 3 | "trash_scores": {}, 4 | "name": "German DL", 5 | "includeCustomFormatWhenRenaming": true, 6 | "specifications": [ 7 | { 8 | "name": "German DL", 9 | "implementation": "ReleaseTitleSpecification", 10 | "negate": false, 11 | "required": true, 12 | "fields": { 13 | "value": "(?i)german\\s*\\.?dl|(?<=\\bGerman\\b.*)(?; 9 | } 10 | 11 | export interface RootFolderSyncResult { 12 | added: number; 13 | removed: number; 14 | updated: number; 15 | } 16 | -------------------------------------------------------------------------------- /tests/samples/quality_with_grouping.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WEB 1080p", 3 | "items": [ 4 | { 5 | "quality": { 6 | "id": 15, 7 | "name": "WEBRip-1080p", 8 | "source": "webRip", 9 | "resolution": 1080 10 | }, 11 | "items": [], 12 | "allowed": true 13 | }, 14 | { 15 | "quality": { 16 | "id": 3, 17 | "name": "WEBDL-1080p", 18 | "source": "web", 19 | "resolution": 1080 20 | }, 21 | "items": [], 22 | "allowed": true 23 | } 24 | ], 25 | "allowed": true, 26 | "id": 1002 27 | } 28 | -------------------------------------------------------------------------------- /examples/full/templates/radarr-quality.yml: -------------------------------------------------------------------------------- 1 | quality_profiles: 2 | - name: ExampleProfile 3 | reset_unmatched_scores: 4 | enabled: true 5 | upgrade: 6 | allowed: true 7 | until_quality: WEB 2160p 8 | until_score: 1000 9 | min_format_score: 0 10 | quality_sort: top 11 | qualities: 12 | - name: Remux-2160p 13 | - name: WEB 2160p 14 | qualities: 15 | - WEBDL-2160p 16 | - WEBRip-2160p 17 | - name: Remux-1080p 18 | - name: WEB 1080p 19 | qualities: 20 | - WEBDL-1080p 21 | - WEBRip-1080p 22 | -------------------------------------------------------------------------------- /examples/full/templates/whisparr.yml: -------------------------------------------------------------------------------- 1 | quality_profiles: 2 | - name: ExampleProfile 3 | reset_unmatched_scores: 4 | enabled: true 5 | upgrade: 6 | allowed: true 7 | until_quality: WEB 2160p 8 | until_score: 1000 9 | min_format_score: 5 10 | quality_sort: top 11 | qualities: 12 | - name: VR 13 | - name: Remux-2160p 14 | - name: WEB 2160p 15 | qualities: 16 | - WEBDL-2160p 17 | - WEBRip-2160p 18 | - name: Remux-1080p 19 | - name: WEB 1080p 20 | qualities: 21 | - WEBDL-1080p 22 | - WEBRip-1080p 23 | -------------------------------------------------------------------------------- /src/tags.ts: -------------------------------------------------------------------------------- 1 | import { getUnifiedClient } from "./clients/unified-client"; 2 | import { logger } from "./logger"; 3 | import { InputConfigDelayProfile } from "./types/config.types"; 4 | import { MergedDelayProfileResource, MergedTagResource } from "./__generated__/mergedTypes"; 5 | import { getEnvs } from "./env"; 6 | 7 | export const loadServerTags = async (): Promise => { 8 | if (getEnvs().LOAD_LOCAL_SAMPLES) { 9 | throw new Error("Local sample loading for tags is not implemented yet."); 10 | } 11 | const api = getUnifiedClient(); 12 | const serverObjects = await api.getTags(); 13 | return serverObjects; 14 | }; 15 | -------------------------------------------------------------------------------- /examples/full/lidarr.xml: -------------------------------------------------------------------------------- 1 | 2 | * 3 | 8686 4 | 6868 5 | False 6 | True 7 | 713067b7319045df9c7fa6847d8ce717 8 | Basic 9 | DisabledForLocalAddresses 10 | master 11 | info 12 | 13 | 14 | 15 | Lidarr 16 | Docker 17 | -------------------------------------------------------------------------------- /examples/full/radarr.xml: -------------------------------------------------------------------------------- 1 | 2 | * 3 | 7878 4 | 9898 5 | False 6 | True 7 | 0daa3a2b940f4e08bac991e9a30e9e12 8 | Basic 9 | DisabledForLocalAddresses 10 | master 11 | info 12 | 13 | 14 | 15 | Radarr 16 | Docker 17 | -------------------------------------------------------------------------------- /examples/full/sonarr.xml: -------------------------------------------------------------------------------- 1 | 2 | * 3 | 8989 4 | 9898 5 | False 6 | True 7 | 5e792b7e0fe14f58b8e92bf0902d4a44 8 | Basic 9 | DisabledForLocalAddresses 10 | main 11 | info 12 | 13 | 14 | 15 | Sonarr 16 | Docker 17 | -------------------------------------------------------------------------------- /examples/full/cfs/custom-size-bigger-40gb.json: -------------------------------------------------------------------------------- 1 | { 2 | "trash_id": "custom-size-more-40gb", 3 | "trash_scores": { 4 | "default": -10000 5 | }, 6 | "trash_description": "Size: Block sizes over 40GB", 7 | "custom_inf": "Does not work because SizeSpecification is not supported by recyclarr", 8 | "name": "Size: Block More 40GB", 9 | "includeCustomFormatWhenRenaming": false, 10 | "specifications": [ 11 | { 12 | "name": "Size", 13 | "implementation": "SizeSpecification", 14 | "negate": false, 15 | "required": true, 16 | "fields": { 17 | "min": 1, 18 | "max": 9 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /examples/full/readarr.xml: -------------------------------------------------------------------------------- 1 | 2 | * 3 | 8787 4 | 6868 5 | False 6 | True 7 | e001718ba38a424d8aae7cb391b5d27a 8 | Basic 9 | DisabledForLocalAddresses 10 | develop 11 | debug 12 | 13 | 14 | 15 | Readarr 16 | Docker 17 | -------------------------------------------------------------------------------- /examples/full/whisparr.xml: -------------------------------------------------------------------------------- 1 | 2 | * 3 | 6969 4 | 9898 5 | False 6 | True 7 | 2ebd18db81e14c2d98d06ef4b865aaa8 8 | Basic 9 | DisabledForLocalAddresses 10 | eros 11 | info 12 | 13 | 14 | 15 | Whisparr 16 | Docker 17 | -------------------------------------------------------------------------------- /examples/scheduled/radarr.xml: -------------------------------------------------------------------------------- 1 | 2 | * 3 | 7878 4 | 9898 5 | False 6 | True 7 | 0daa3a2b940f4e08bac991e9a30e9e12 8 | Basic 9 | DisabledForLocalAddresses 10 | master 11 | info 12 | 13 | 14 | 15 | Radarr 16 | Docker 17 | -------------------------------------------------------------------------------- /examples/scheduled/ofelia.ini: -------------------------------------------------------------------------------- 1 | ; Example of how to run on existing container which will be restarted 2 | [job-run "run-configarr-existing-container"] 3 | schedule = @every 10s 4 | container = configarr-reused 5 | 6 | ; Creates new container and executes with given parameters 7 | [job-run "run-configarr-new-container"] 8 | schedule = @every 10s 9 | image = ghcr.io/raydak-labs/configarr:latest 10 | ; Full path for volume 11 | volume = /tmp/configarr/full/path/config:/app/config 12 | volume = /tmp/configarr/full/path/dockerrepos:/app/repos 13 | ;volume = /tmp/configarr/full/path/cfs:/app/cfs 14 | ;volume = /tmp/configarr/full/path/templates:/app/templates 15 | network = configarr-full 16 | -------------------------------------------------------------------------------- /src/types/recyclarr.types.ts: -------------------------------------------------------------------------------- 1 | import { ArrType } from "./common.types"; 2 | import { ConfigArrInstance, InputConfigCustomFormat } from "./config.types"; 3 | 4 | export type RecyclarrCustomFormats = Partial> & {}; 5 | 6 | export type RecyclarrConfigInstance = OmitTyped & { 7 | custom_formats: RecyclarrCustomFormats[]; 8 | }; 9 | 10 | export type RecyclarrTemplates = Partial< 11 | Pick 12 | >; 13 | 14 | export type RecyclarrArrSupported = Subset; 15 | -------------------------------------------------------------------------------- /examples/scheduled/config/config.yml: -------------------------------------------------------------------------------- 1 | #trashGuideUrl: https://github.com/BlackDark/fork-TRASH-Guides 2 | #recyclarrConfigUrl: https://github.com/BlackDark/fork-recyclarr-configs 3 | localCustomFormatsPath: /app/cfs 4 | localConfigTemplatesPath: /app/templates 5 | 6 | radarr: 7 | instance1: 8 | # Set the URL/API Key to your actual instance 9 | base_url: http://radarr:7878 10 | api_key: !secret RADARR_API_KEY 11 | 12 | quality_definition: 13 | type: movie 14 | 15 | include: 16 | - template: radarr-quality-definition-movie 17 | - template: radarr-quality-profile-hd-bluray-web 18 | - template: radarr-custom-formats-hd-bluray-web 19 | 20 | custom_formats: [] 21 | -------------------------------------------------------------------------------- /examples/scheduled/docker-compose.ofelia.yml: -------------------------------------------------------------------------------- 1 | services: 2 | configarr: 3 | container_name: configarr-reused 4 | image: ghcr.io/raydak-labs/configarr:latest 5 | networks: 6 | - configarr-full 7 | volumes: 8 | - ./config:/app/config 9 | - ./dockerrepos:/app/repos 10 | #- ./cfs:/app/cfs 11 | #- ./templates:/app/templates 12 | 13 | ofelia: 14 | image: mcuadros/ofelia:latest 15 | command: daemon --config=/opt/config.ini 16 | #command: daemon --docker 17 | volumes: 18 | - /var/run/docker.sock:/var/run/docker.sock:ro 19 | - ./ofelia.ini:/opt/config.ini 20 | 21 | networks: 22 | configarr-full: 23 | name: configarr-full 24 | external: true 25 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches-ignore: 9 | - prod 10 | 11 | jobs: 12 | test: 13 | name: Lint & Test 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v6 18 | 19 | - uses: pnpm/action-setup@v4 20 | with: 21 | version: latest 22 | 23 | - uses: actions/setup-node@v6 24 | with: 25 | node-version: lts/* 26 | cache: pnpm 27 | 28 | - run: pnpm install 29 | 30 | - name: Lint 31 | run: pnpm lint 32 | 33 | - name: Typecheck 34 | run: pnpm typecheck 35 | 36 | - name: Test 37 | run: pnpm test 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | // breaks esbuild import 6 | // "baseUrl": ".", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "isolatedModules": true, 10 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 11 | "module": "ESNext", 12 | "moduleResolution": "Bundler", 13 | "noUncheckedIndexedAccess": true, 14 | // "paths": { 15 | // "~/*": ["./src/*"] 16 | // }, 17 | "resolveJsonModule": true, 18 | "skipLibCheck": true, 19 | "strict": true, 20 | "strictNullChecks": true, 21 | "target": "ESNext", 22 | "noEmit": true 23 | }, 24 | "exclude": ["./dist", "./docs"], 25 | "include": ["**/*.ts", "**/*.tsx"] 26 | } 27 | -------------------------------------------------------------------------------- /examples/full/docker-compose.local.yml: -------------------------------------------------------------------------------- 1 | services: 2 | configarr: 3 | #image: ghcr.io/raydak-labs/configarr:1.2.1 4 | image: configarr:local 5 | build: 6 | context: ../.. 7 | dockerfile: Dockerfile 8 | target: dev 9 | networks: 10 | - configarr-full 11 | volumes: 12 | - ./config:/app/config 13 | - ./dockerrepos:/app/repos 14 | - ./cfs:/app/cfs 15 | - ./templates:/app/templates 16 | - ../../src:/app/src 17 | - ./debug:/app/debug 18 | - ./data:/data 19 | environment: 20 | LOG_LEVEL: debug 21 | DEBUG_CREATE_FILES: false 22 | # If you want to map all data to new path instead of /app 23 | #ROOT_PATH: /data 24 | 25 | networks: 26 | configarr-full: 27 | name: configarr-full 28 | external: true 29 | -------------------------------------------------------------------------------- /src/metadataProfiles/metadataProfile.types.ts: -------------------------------------------------------------------------------- 1 | import { InputConfigMetadataProfile } from "../types/config.types"; 2 | 3 | // Common interface for all metadata profile resources 4 | // All metadata profiles must have at least id and name 5 | export interface BaseMetadataProfileResource { 6 | id?: number; 7 | name?: string | null; 8 | } 9 | 10 | // Shared types for metadata profile operations 11 | // Generic type T represents the specific MetadataProfileResource type (Lidarr, Readarr, etc.) 12 | export interface MetadataProfileDiff { 13 | missingOnServer: InputConfigMetadataProfile[]; 14 | changed: Array<{ config: InputConfigMetadataProfile; server: T }>; 15 | noChanges: T[]; 16 | } 17 | 18 | export interface MetadataProfileSyncResult { 19 | added: number; 20 | removed: number; 21 | updated: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/downloadClients/downloadClientSyncer.ts: -------------------------------------------------------------------------------- 1 | import { ServerCache } from "../cache"; 2 | import { ArrType } from "../types/common.types"; 3 | import { MergedConfigInstance } from "../types/config.types"; 4 | import { BaseDownloadClientSync } from "./downloadClientBase"; 5 | import { GenericDownloadClientSync } from "./downloadClientGeneric"; 6 | import { DownloadClientSyncResult } from "../types/download-client.types"; 7 | 8 | function createDownloadClientSync(arrType: ArrType): BaseDownloadClientSync { 9 | return new GenericDownloadClientSync(arrType); 10 | } 11 | 12 | export async function syncDownloadClients( 13 | arrType: ArrType, 14 | config: MergedConfigInstance, 15 | serverCache: ServerCache, 16 | ): Promise { 17 | const sync = createDownloadClientSync(arrType); 18 | return sync.syncDownloadClients(config, serverCache); 19 | } 20 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Configarr flake"; 3 | 4 | inputs = { 5 | flake-parts.url = "github:hercules-ci/flake-parts"; 6 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 7 | systems.url = "github:nix-systems/default"; 8 | }; 9 | 10 | outputs = { 11 | flake-parts, 12 | nixpkgs, 13 | ... 14 | } @ inputs: 15 | flake-parts.lib.mkFlake {inherit inputs;} { 16 | systems = [ 17 | "x86_64-linux" 18 | "aarch64-linux" 19 | "x86_64-darwin" 20 | "aarch64-darwin" 21 | ]; 22 | 23 | flake = { 24 | nixosModules.default = ./pkgs/nix/module; 25 | }; 26 | 27 | perSystem = {system, ...}: let 28 | pkgs = nixpkgs.legacyPackages.${system}; 29 | in { 30 | packages.default = import ./pkgs/nix/package.nix { 31 | inherit pkgs; 32 | inherit (pkgs) lib; 33 | }; 34 | }; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /custom/cfs/lang-not-en-de.json: -------------------------------------------------------------------------------- 1 | { 2 | "trash_id": "lang-not-en-de-version1", 3 | "trash_scores": {}, 4 | "name": "Language: Not ENG/GER", 5 | "includeCustomFormatWhenRenaming": false, 6 | "specifications": [ 7 | { 8 | "name": "Not English Language", 9 | "implementation": "LanguageSpecification", 10 | "negate": true, 11 | "required": true, 12 | "fields": { 13 | "value": 1 14 | } 15 | }, 16 | { 17 | "name": "Not German Language", 18 | "implementation": "LanguageSpecification", 19 | "negate": true, 20 | "required": true, 21 | "fields": { 22 | "value": 4 23 | } 24 | }, 25 | { 26 | "name": "Not German in Title", 27 | "implementation": "ReleaseTitleSpecification", 28 | "negate": true, 29 | "required": true, 30 | "fields": { 31 | "value": "(?i)\\bgerman\\b" 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Configarr documentation 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ pnpm 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ pnpm start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ pnpm build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true pnpm deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= pnpm deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docs/static/img/experiment.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/sidebars.ts: -------------------------------------------------------------------------------- 1 | import type { SidebarsConfig } from "@docusaurus/plugin-content-docs"; 2 | 3 | // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) 4 | 5 | /** 6 | * Creating a sidebar enables you to: 7 | - create an ordered group of docs 8 | - render a sidebar for each doc of that group 9 | - provide next/previous navigation 10 | 11 | The sidebars can be generated from the filesystem, or explicitly defined here. 12 | 13 | Create as many sidebars as you want. 14 | */ 15 | const sidebars: SidebarsConfig = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{ type: "autogenerated", dirName: "." }], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | 'intro', 23 | 'hello', 24 | { 25 | type: 'category', 26 | label: 'Tutorial', 27 | items: ['tutorial-basics/create-a-document'], 28 | }, 29 | ], 30 | */ 31 | }; 32 | 33 | export default sidebars; 34 | -------------------------------------------------------------------------------- /examples/full/README.md: -------------------------------------------------------------------------------- 1 | # Configarr - Full example 2 | 3 | This example contains every feature provided by configarr. 4 | 5 | 1. Start arr containers with `docker-compose up -d` 6 | - Create network for containers 7 | - Creates sonarr instance 8 | - Creates radarr instance 9 | - API keys are provided with the `xml` configs 10 | 2. Run configarr with `docker-compose -f docker-compose.jobs.yml run --rm configarr` 11 | 12 | URLs: 13 | 14 | - sonarr: http://localhost:6500 15 | - radarr: http://localhost:6501 16 | 17 | Cleanup: `docker-compose down -v` 18 | 19 | ## Development 20 | 21 | You can also use this full example template as testing environment by utilizing the `docker-compose.local.yml` file. 22 | 23 | - Build image: `docker-compose -f docker-compose.local.yml build` 24 | - Run image: `docker-compose -f docker-compose.local.yml run --rm configarr` 25 | - This will run always with the current code changes; no need to rebuild for simple code changes because the code will be mounted into the container 26 | -------------------------------------------------------------------------------- /custom/cfs/lang-de-dl-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "trash_id": "lang-de-dl-2-version1", 3 | "trash_scores": {}, 4 | "name": "German DL 2", 5 | "includeCustomFormatWhenRenaming": false, 6 | "specifications": [ 7 | { 8 | "name": "NOT German DL", 9 | "implementation": "ReleaseTitleSpecification", 10 | "negate": true, 11 | "required": true, 12 | "fields": { 13 | "value": "(?i)german\\s*\\.?dl|(?<=\\bGerman\\b.*)(? { 22 | if (!rootFolders) { 23 | return { added: 0, removed: 0, updated: 0 }; 24 | } 25 | 26 | const sync = createRootFolderSync(arrType); 27 | return sync.syncRootFolders(rootFolders, serverCache); 28 | } 29 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | 4 | "configurations": [ 5 | { 6 | "command": "pnpm start", 7 | "name": "pnpm start", 8 | "request": "launch", 9 | "type": "node-terminal" 10 | }, 11 | { 12 | "name": "tsx", 13 | "type": "node", 14 | "request": "launch", 15 | 16 | // Debug current file in VSCode 17 | "program": "${file}", 18 | 19 | /* 20 | Path to tsx binary 21 | Assuming locally installed 22 | */ 23 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/tsx", 24 | 25 | /* 26 | Open terminal when debugging starts (Optional) 27 | Useful to see console.logs 28 | */ 29 | "console": "integratedTerminal", 30 | "internalConsoleOptions": "neverOpen", 31 | 32 | // Files to exclude from debugger (e.g. call stack) 33 | "skipFiles": [ 34 | // Node.js internal core modules 35 | "/**", 36 | 37 | // Ignore all dependencies (optional) 38 | "${workspaceFolder}/node_modules/**" 39 | ] 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "npm": false, 3 | "git": { 4 | "commitMessage": "chore: release ${version}", 5 | "requireCleanWorkingDir": false 6 | }, 7 | "github": { 8 | "release": true, 9 | "comments": { 10 | "submit": false 11 | } 12 | }, 13 | "plugins": { 14 | "@release-it/conventional-changelog": { 15 | "preset": { 16 | "name": "conventionalcommits", 17 | "types": [ 18 | { 19 | "type": "feat", 20 | "section": "Features" 21 | }, 22 | { 23 | "type": "fix", 24 | "section": "Bug Fixes" 25 | }, 26 | { 27 | "type": "refactor", 28 | "section": "(internal) Refactorings" 29 | } 30 | ] 31 | }, 32 | "infile": "CHANGELOG.md", 33 | "header": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n" 34 | } 35 | }, 36 | "hooks": { 37 | "before:git:release": ["pnpm exec prettier . --write", "git add .", "pnpm run lint"], 38 | "after:release": "echo \"RELEASED_VERSION=${version}\" >> $GITHUB_ENV" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/__generated__/lidarr/Logout.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Logout { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags Authentication 26 | * @name LogoutList 27 | * @request GET:/logout 28 | * @secure 29 | */ 30 | logoutList = (params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/logout`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/radarr/Logout.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Logout { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags Authentication 26 | * @name LogoutList 27 | * @request GET:/logout 28 | * @secure 29 | */ 30 | logoutList = (params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/logout`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/sonarr/Logout.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Logout { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags Authentication 26 | * @name LogoutList 27 | * @request GET:/logout 28 | * @secure 29 | */ 30 | logoutList = (params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/logout`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/lidarr/Path.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Path { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags StaticResource 26 | * @name GetPath 27 | * @request GET:/{path} 28 | * @secure 29 | */ 30 | getPath = (path: string, params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/${path}`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/radarr/Path.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Path { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags StaticResource 26 | * @name GetPath 27 | * @request GET:/{path} 28 | * @secure 29 | */ 30 | getPath = (path: string, params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/${path}`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/readarr/Logout.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Logout { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags Authentication 26 | * @name LogoutList 27 | * @request GET:/logout 28 | * @secure 29 | */ 30 | logoutList = (params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/logout`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/sonarr/Path.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Path { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags StaticResource 26 | * @name GetPath 27 | * @request GET:/{path} 28 | * @secure 29 | */ 30 | getPath = (path: string, params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/${path}`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/whisparr/Logout.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Logout { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags Authentication 26 | * @name LogoutList 27 | * @request GET:/logout 28 | * @secure 29 | */ 30 | logoutList = (params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/logout`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/readarr/Path.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Path { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags StaticResource 26 | * @name GetPath 27 | * @request GET:/{path} 28 | * @secure 29 | */ 30 | getPath = (path: string, params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/${path}`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/whisparr/Path.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Path { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags StaticResource 26 | * @name GetPath 27 | * @request GET:/{path} 28 | * @secure 29 | */ 30 | getPath = (path: string, params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/${path}`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/release-it.yml: -------------------------------------------------------------------------------- 1 | name: release-it 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | release: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Generate a token 11 | id: generate_token 12 | uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0 13 | with: 14 | app_id: ${{ secrets.RELEASER_APP_ID }} 15 | private_key: ${{ secrets.RELEASER_PRIVATE_KEY }} 16 | 17 | - name: Checkout code 18 | uses: actions/checkout@v6 19 | with: 20 | token: ${{ steps.generate_token.outputs.token }} 21 | fetch-depth: 0 22 | 23 | - name: git config 24 | run: | 25 | git config user.email "raydak-releaser@users.noreply.github.com" 26 | git config user.name "raydak-releaser[bot]" 27 | 28 | - uses: pnpm/action-setup@v4 29 | with: 30 | version: latest 31 | 32 | - uses: actions/setup-node@v6 33 | with: 34 | node-version: lts/* 35 | cache: pnpm 36 | 37 | - run: pnpm install 38 | 39 | - run: pnpm release-it --ci 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | -------------------------------------------------------------------------------- /src/__generated__/lidarr/Content.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Content { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags StaticResource 26 | * @name ContentDetail 27 | * @request GET:/content/{path} 28 | * @secure 29 | */ 30 | contentDetail = (path: string, params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/content/${path}`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/radarr/Content.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Content { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags StaticResource 26 | * @name ContentDetail 27 | * @request GET:/content/{path} 28 | * @secure 29 | */ 30 | contentDetail = (path: string, params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/content/${path}`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/readarr/Content.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Content { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags StaticResource 26 | * @name ContentDetail 27 | * @request GET:/content/{path} 28 | * @secure 29 | */ 30 | contentDetail = (path: string, params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/content/${path}`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/sonarr/Content.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Content { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags StaticResource 26 | * @name ContentDetail 27 | * @request GET:/content/{path} 28 | * @secure 29 | */ 30 | contentDetail = (path: string, params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/content/${path}`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/whisparr/Content.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Content { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags StaticResource 26 | * @name ContentDetail 27 | * @request GET:/content/{path} 28 | * @secure 29 | */ 30 | contentDetail = (path: string, params: RequestParams = {}) => 31 | this.http.request({ 32 | path: `/content/${path}`, 33 | method: "GET", 34 | secure: true, 35 | ...params, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/__generated__/whisparr/Ping.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | import { PingResource } from "./data-contracts"; 15 | 16 | export class Ping { 17 | http: HttpClient; 18 | 19 | constructor(http: HttpClient) { 20 | this.http = http; 21 | } 22 | 23 | /** 24 | * No description 25 | * 26 | * @tags Ping 27 | * @name PingList 28 | * @request GET:/ping 29 | * @secure 30 | */ 31 | pingList = (params: RequestParams = {}) => 32 | this.http.request({ 33 | path: `/ping`, 34 | method: "GET", 35 | secure: true, 36 | format: "json", 37 | ...params, 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /examples/scheduled/docker-compose.cron.yml: -------------------------------------------------------------------------------- 1 | services: 2 | configarr: 3 | image: ghcr.io/raydak-labs/configarr:latest 4 | networks: 5 | - configarr-full 6 | volumes: 7 | - ${CONFIGARR_FULL_PATH:?"missing"}/config:/app/config 8 | - ${CONFIGARR_FULL_PATH:?"missing"}/dockerrepos:/app/repos 9 | #- ${CONFIGARR_FULL_PATH:?"missing"}/cfs:/app/cfs 10 | #- ${CONFIGARR_FULL_PATH:?"missing"}/templates:/app/templates 11 | 12 | configarr-run: 13 | image: ghcr.io/raydak-labs/configarr:latest 14 | networks: 15 | - configarr-full 16 | volumes: 17 | - ${CONFIGARR_FULL_PATH:?"missing"}/config:/app/config 18 | - ${CONFIGARR_FULL_PATH:?"missing"}/dockerrepos:/app/repos 19 | #- ${CONFIGARR_FULL_PATH:?"missing"}/cfs:/app/cfs 20 | #- ${CONFIGARR_FULL_PATH:?"missing"}/templates:/app/templates 21 | 22 | cron: 23 | image: blackdark93/dockerfiles-cron-dind:main 24 | volumes: 25 | - /var/run/docker.sock:/var/run/docker.sock:ro 26 | - ${CONFIGARR_FULL_PATH:?"missing"}/cron:/app/schedules:ro 27 | - ${CONFIGARR_FULL_PATH:?"missing"}/docker-compose.cron.yml:/app/docker-compose.yaml:ro 28 | 29 | networks: 30 | configarr-full: 31 | name: configarr-full 32 | external: true 33 | -------------------------------------------------------------------------------- /DEV.md: -------------------------------------------------------------------------------- 1 | # Development related README 2 | 3 | ## Compiling to binary 4 | 5 | ### bun (trying) 6 | 7 | - compiles and treeshakes code already 8 | - `bun build src/index.ts --compile --outfile cli-bun` 9 | - size: 61mb 10 | 11 | ```bash 12 | # what could be done but now sure if any real benefit 13 | bun build src/index.ts \ 14 | --compile \ 15 | --outfile configarr-${{ matrix.platform }} \ 16 | --define CONFIGARR_VERSION=\"${{ steps.vars.outputs.version }}\" \ 17 | --define BUILD_TIME=\"${{ steps.vars.outputs.build_time }}\" \ 18 | --define GITHUB_RUN_ID=\"${{ steps.vars.outputs.run_id }}\" \ 19 | --define GITHUB_REPO=\"${{ steps.vars.outputs.repo }}\" \ 20 | --define GITHUB_SHA=\"${{ steps.vars.outputs.sha }}\" \ 21 | --define BUILD_PLATFORM=\"${{ matrix.platform }}\" \ 22 | --bytecode \ 23 | --production \ 24 | --minify \ 25 | --minify-syntax \ 26 | --minify-whitespace \ 27 | --minify-identifiers \ 28 | ${{ github.event.inputs.baseline == 'true' && '--target=bun' || '' }} 29 | ``` 30 | 31 | ### deno 32 | 33 | - `pnpm build` 34 | - `deno compile --no-check --sloppy-imports --output cli-deno --no-npm --allow-all bundle.cjs` 35 | - size: 77mb 36 | 37 | ## docker 38 | 39 | - we could probably just use scratch as base? with the binary? 40 | -------------------------------------------------------------------------------- /config.yml.template: -------------------------------------------------------------------------------- 1 | trashGuideUrl: https://github.com/TRaSH-Guides/Guides 2 | #trashRevision: master # Optional to specify sha 3 | recyclarrConfigUrl: https://github.com/recyclarr/config-templates 4 | #recyclarrRevision: master # Optional to specify sha 5 | 6 | # Optional if you want to add custom formats locally 7 | #localCustomFormatsPath: ./custom/cfs 8 | #localConfigTemplatesPath: /app/templates 9 | 10 | sonarr: 11 | series: 12 | # Set the URL/API Key to your actual instance 13 | base_url: http://localhost:8989 14 | api_key: !secret SONARR_API_KEY 15 | 16 | # Quality definitions from the guide to sync to Sonarr. Choices: series, anime 17 | quality_definition: 18 | type: series 19 | 20 | include: 21 | # Comment out any of the following includes to disable them 22 | #### WEB-1080p 23 | - template: sonarr-quality-definition-series 24 | - template: sonarr-v4-quality-profile-web-1080p 25 | - template: sonarr-v4-custom-formats-web-1080p 26 | 27 | #### WEB-2160p 28 | - template: sonarr-v4-quality-profile-web-2160p 29 | - template: sonarr-v4-custom-formats-web-2160p 30 | 31 | # Custom Formats: https://recyclarr.dev/wiki/yaml/config-reference/custom-formats/ 32 | custom_formats: [] 33 | 34 | radarr: 35 | instance1: 36 | define: true 37 | -------------------------------------------------------------------------------- /tests/samples/single_custom_format.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "name": "BR-DISK", 4 | "includeCustomFormatWhenRenaming": false, 5 | "specifications": [ 6 | { 7 | "name": "BR-DISK", 8 | "implementation": "ReleaseTitleSpecification", 9 | "implementationName": "Release Title", 10 | "infoLink": "https://wiki.servarr.com/sonarr/settings#custom-formats-2", 11 | "negate": false, 12 | "required": true, 13 | "fields": [ 14 | { 15 | "order": 0, 16 | "name": "value", 17 | "label": "Regular Expression", 18 | "helpText": "Custom Format RegEx is Case Insensitive", 19 | "value": "^(?!.*\\b((? { 27 | const sync = createMetadataProfileSync(arrType); 28 | 29 | if (sync == null) { 30 | return; 31 | } 32 | 33 | await sync.syncMetadataProfiles(config, serverCache); 34 | } 35 | -------------------------------------------------------------------------------- /docs/docs/installation/_include/docker-basic-conf.yml: -------------------------------------------------------------------------------- 1 | #trashGuideUrl: https://github.com/BlackDark/fork-TRASH-Guides 2 | #recyclarrConfigUrl: https://github.com/BlackDark/fork-recyclarr-configs 3 | localCustomFormatsPath: /app/cfs 4 | localConfigTemplatesPath: /app/templates 5 | 6 | customFormatDefinitions: 7 | - trash_id: example-in-config-cf 8 | trash_scores: 9 | default: -10000 10 | trash_description: "Language: German Only" 11 | name: "Language: Not German" 12 | includeCustomFormatWhenRenaming: false 13 | specifications: 14 | - name: Not German Language 15 | implementation: LanguageSpecification 16 | negate: true 17 | required: false 18 | fields: 19 | value: 4 20 | 21 | sonarr: 22 | instance1: 23 | # Set the URL/API Key to your actual instance 24 | base_url: http://sonarr:8989 25 | api_key: !secret SONARR_API_KEY 26 | 27 | quality_definition: 28 | type: series 29 | 30 | include: 31 | #### Custom 32 | - template: sonarr-cf # template name 33 | - template: sonarr-quality 34 | 35 | custom_formats: 36 | # Movie Versions 37 | - trash_ids: 38 | - 9f6cbff8cfe4ebbc1bde14c7b7bec0de # IMAX Enhanced 39 | quality_profiles: 40 | - name: ExampleProfile 41 | # score: 0 # Uncomment this line to disable prioritised IMAX Enhanced releases 42 | 43 | radarr: {} # no radarr instance 44 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "configarr", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc" 16 | }, 17 | "dependencies": { 18 | "@docusaurus/core": "3.9.2", 19 | "@docusaurus/preset-classic": "3.9.2", 20 | "@mdx-js/react": "3.1.1", 21 | "clsx": "2.1.1", 22 | "docusaurus-lunr-search": "3.6.1", 23 | "lunr": "2.3.9", 24 | "prism-react-renderer": "2.4.1", 25 | "raw-loader": "4.0.2", 26 | "react": "19.2.0", 27 | "react-dom": "19.2.0" 28 | }, 29 | "devDependencies": { 30 | "@docusaurus/module-type-aliases": "3.9.2", 31 | "@docusaurus/tsconfig": "3.9.2", 32 | "@docusaurus/types": "3.9.2", 33 | "typescript": "5.9.3" 34 | }, 35 | "browserslist": { 36 | "production": [ 37 | ">0.5%", 38 | "not dead", 39 | "not op_mini all" 40 | ], 41 | "development": [ 42 | "last 3 chrome version", 43 | "last 3 firefox version", 44 | "last 5 safari version" 45 | ] 46 | }, 47 | "engines": { 48 | "node": "25.2.1" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "configarr", 3 | "version": "0.0.0", 4 | "description": "", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/raydak-labs/configarr.git" 8 | }, 9 | "type": "module", 10 | "scripts": { 11 | "build": "tsx esbuild.ts", 12 | "coverage": "vitest run --coverage", 13 | "generateApi": "tsx generate-api.ts && prettier src/__generated__ --write", 14 | "lint": "prettier --log-level error --check .", 15 | "lint:fix": "prettier --list-different --write --log-level error .", 16 | "start": "tsx src/index.ts", 17 | "test": "vitest run", 18 | "test:watch": "vitest", 19 | "typecheck": "tsc --noEmit", 20 | "release": "release-it" 21 | }, 22 | "author": "", 23 | "license": "ISC", 24 | "dependencies": { 25 | "dotenv": "17.2.3", 26 | "ky": "1.14.0", 27 | "pino": "10.1.0", 28 | "pino-pretty": "13.1.3", 29 | "simple-git": "3.30.0", 30 | "tsx": "4.21.0", 31 | "yaml": "2.8.2", 32 | "zod": "4.1.13" 33 | }, 34 | "devDependencies": { 35 | "@hyrious/esbuild-plugin-commonjs": "0.2.6", 36 | "@playwright/test": "1.57.0", 37 | "@release-it/conventional-changelog": "^10.0.4", 38 | "@types/node": "24.10.4", 39 | "@vitest/coverage-v8": "4.0.15", 40 | "esbuild": "0.27.1", 41 | "prettier": "3.7.4", 42 | "release-it": "19.1.0", 43 | "swagger-typescript-api": "13.2.16", 44 | "typescript": "5.9.3", 45 | "vitest": "4.0.15" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/docs/configuration/scheduled.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | title: Scheduling 4 | description: "How to run configarr regulary/schedueld" 5 | keywords: [configarr configuration, schedule, scheduler, regular, cron] 6 | --- 7 | 8 | # Scheduling configarr 9 | 10 | This section describes how you could run configarr in a scheduled manner. 11 | Depending on your used deployment or third party ([check](../installation/third-party.md)) you can have different methods for running in schedule. 12 | 13 | :::info 14 | Configarr does not support scheduled execution inside the container and most likely never will. 15 | Scheduling functionalities should be something which is handled outside of the container and something which should be bundled inside each app. 16 | ::: 17 | 18 | ## Kubernetes 19 | 20 | Kubernetes support for scheduling jobs is straigthforward. 21 | We have explicit resources for this tasks: `CronJobs` 22 | See [Kubernetes Setup](/docs/installation/kubernetes) for more information. 23 | 24 | ## Docker 25 | 26 | For docker and docker-compose we do not have explicit functionalities. 27 | Therefore we have to create our own scheduled tasks. 28 | There are different ways we could achieve this: 29 | 30 | - default cron scheduler on linux systems 31 | - running scheduler containers which executes other docker containers 32 | 33 | We have create examples for how to run the container based solutions. 34 | Check [examples/scheduled](/docs/examples#scheduled-example) for more information. 35 | -------------------------------------------------------------------------------- /pkgs/nix/module/options.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | pkgs, 4 | ... 5 | }: { 6 | options.services.configarr = { 7 | config = lib.mkOption { 8 | default = ""; 9 | description = "YAML configuration."; 10 | type = lib.types.str; 11 | }; 12 | 13 | dataDir = lib.mkOption { 14 | default = "/var/lib/configarr"; 15 | description = "Working directory for Configarr (repos/, config/, etc.)."; 16 | type = lib.types.path; 17 | }; 18 | 19 | enable = lib.mkEnableOption "Configarr synchronization service"; 20 | 21 | environmentFile = lib.mkOption { 22 | default = null; 23 | description = '' 24 | Environment file as defined in {manpage}`systemd.exec(5)`. 25 | ''; 26 | type = lib.types.nullOr lib.types.path; 27 | }; 28 | 29 | group = lib.mkOption { 30 | default = "configarr"; 31 | description = "Group for the Configarr service."; 32 | type = lib.types.str; 33 | }; 34 | 35 | package = lib.mkOption { 36 | default = import ../package.nix {inherit lib pkgs;}; 37 | description = "Package to use"; 38 | type = lib.types.package; 39 | }; 40 | 41 | schedule = lib.mkOption { 42 | default = "daily"; 43 | description = "Run interval for the timer."; 44 | type = lib.types.str; 45 | }; 46 | 47 | user = lib.mkOption { 48 | default = "configarr"; 49 | description = "User to run the Configarr service as."; 50 | type = lib.types.str; 51 | }; 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /docs/docs/installation/binary.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | title: Binary 4 | description: "Run configarr as binary file" 5 | keywords: [configarr binary, configarr executable] 6 | --- 7 | 8 | import Admonition from "@theme/Admonition"; 9 | import CodeBlock from "@theme/CodeBlock"; 10 | import DockerBasicConf from "!!raw-loader!./\_include/docker-basic-conf.yml"; 11 | 12 | # Executable / Binary 1.17.0 13 | 14 | With 1.17.0 we have started to distribute configarr additionally as binary files. 15 | This is currently based on [Bun Compilation](https://bun.com/docs/bundler/executables) 16 | 17 | If you have problems or feedback feel free to create an issue to discuss potential problems or optimizations. 18 | 19 | ## Quick Start 20 | 21 | Checkout the releases in [Github](https://github.com/raydak-labs/configarr/releases/). 22 | There we will attach binaries for different architectures and systems. 23 | 24 | The files are probably compressed and need to be extracted. 25 | Afterwards you can run the executable as any other program or tool. 26 | 27 | Need more help? [open an issue](https://github.com/raydak-labs/configarr/issues). 28 | 29 | ## FAQ 30 | 31 | - `On MacOS if I download files the file is corrupt`: If downloaded via browser or something the resulting executable can be quarantined. 32 | - To check attributes: `xattr ./configarr -l` 33 | - To remove quarantine: `xattr -dr com.apple.quarantine ./configarr` 34 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: "https://docs.renovatebot.com/renovate-schema.json", 3 | extends: [ 4 | "config:recommended", 5 | "customManagers:githubActionsVersions", 6 | "schedule:monthly", 7 | ":prConcurrentLimitNone", 8 | ":prHourlyLimitNone", 9 | ":pinVersions", 10 | "security:minimumReleaseAgeNpm", 11 | ], 12 | ignorePaths: [ 13 | "**/node_modules/**", 14 | "**/bower_components/**", 15 | "**/vendor/**", 16 | "**/__tests__/**", 17 | "**/test/**", 18 | "**/tests/**", 19 | "**/__fixtures__/**", 20 | ], 21 | labels: ["dependencies"], 22 | packageRules: [ 23 | { 24 | matchUpdateTypes: ["minor", "patch", "pin"], 25 | automerge: true, 26 | addLabels: ["automerge"], 27 | }, 28 | { 29 | groupName: "devDependencies (non-major)", 30 | matchDepTypes: ["devDependencies"], 31 | matchUpdateTypes: ["patch", "minor"], 32 | }, 33 | { 34 | matchPackageNames: ["swagger-typescript-api"], 35 | groupName: null, 36 | }, 37 | { 38 | groupName: "dependencies (non-major)", 39 | matchDepTypes: ["dependencies"], 40 | matchUpdateTypes: ["patch", "minor"], 41 | }, 42 | { 43 | extends: ["monorepo:docusaurus"], 44 | groupName: "docusaurus monorepo", 45 | matchUpdateTypes: ["digest", "patch", "minor", "major"], 46 | }, 47 | { 48 | matchDatasources: ["docker"], 49 | matchUpdateTypes: ["minor", "patch"], 50 | groupName: "all docker updates (without major)", 51 | groupSlug: "all-docker", 52 | }, 53 | ], 54 | } 55 | -------------------------------------------------------------------------------- /src/__generated__/lidarr/Ping.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | import { PingResource } from "./data-contracts"; 15 | 16 | export class Ping { 17 | http: HttpClient; 18 | 19 | constructor(http: HttpClient) { 20 | this.http = http; 21 | } 22 | 23 | /** 24 | * No description 25 | * 26 | * @tags Ping 27 | * @name PingList 28 | * @request GET:/ping 29 | * @secure 30 | */ 31 | pingList = (params: RequestParams = {}) => 32 | this.http.request({ 33 | path: `/ping`, 34 | method: "GET", 35 | secure: true, 36 | format: "json", 37 | ...params, 38 | }); 39 | /** 40 | * No description 41 | * 42 | * @tags Ping 43 | * @name HeadPing 44 | * @request HEAD:/ping 45 | * @secure 46 | */ 47 | headPing = (params: RequestParams = {}) => 48 | this.http.request({ 49 | path: `/ping`, 50 | method: "HEAD", 51 | secure: true, 52 | format: "json", 53 | ...params, 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /src/__generated__/radarr/Ping.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | import { PingResource } from "./data-contracts"; 15 | 16 | export class Ping { 17 | http: HttpClient; 18 | 19 | constructor(http: HttpClient) { 20 | this.http = http; 21 | } 22 | 23 | /** 24 | * No description 25 | * 26 | * @tags Ping 27 | * @name PingList 28 | * @request GET:/ping 29 | * @secure 30 | */ 31 | pingList = (params: RequestParams = {}) => 32 | this.http.request({ 33 | path: `/ping`, 34 | method: "GET", 35 | secure: true, 36 | format: "json", 37 | ...params, 38 | }); 39 | /** 40 | * No description 41 | * 42 | * @tags Ping 43 | * @name HeadPing 44 | * @request HEAD:/ping 45 | * @secure 46 | */ 47 | headPing = (params: RequestParams = {}) => 48 | this.http.request({ 49 | path: `/ping`, 50 | method: "HEAD", 51 | secure: true, 52 | format: "json", 53 | ...params, 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /src/__generated__/readarr/Ping.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | import { PingResource } from "./data-contracts"; 15 | 16 | export class Ping { 17 | http: HttpClient; 18 | 19 | constructor(http: HttpClient) { 20 | this.http = http; 21 | } 22 | 23 | /** 24 | * No description 25 | * 26 | * @tags Ping 27 | * @name PingList 28 | * @request GET:/ping 29 | * @secure 30 | */ 31 | pingList = (params: RequestParams = {}) => 32 | this.http.request({ 33 | path: `/ping`, 34 | method: "GET", 35 | secure: true, 36 | format: "json", 37 | ...params, 38 | }); 39 | /** 40 | * No description 41 | * 42 | * @tags Ping 43 | * @name HeadPing 44 | * @request HEAD:/ping 45 | * @secure 46 | */ 47 | headPing = (params: RequestParams = {}) => 48 | this.http.request({ 49 | path: `/ping`, 50 | method: "HEAD", 51 | secure: true, 52 | format: "json", 53 | ...params, 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /src/__generated__/sonarr/Ping.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | import { PingResource } from "./data-contracts"; 15 | 16 | export class Ping { 17 | http: HttpClient; 18 | 19 | constructor(http: HttpClient) { 20 | this.http = http; 21 | } 22 | 23 | /** 24 | * No description 25 | * 26 | * @tags Ping 27 | * @name PingList 28 | * @request GET:/ping 29 | * @secure 30 | */ 31 | pingList = (params: RequestParams = {}) => 32 | this.http.request({ 33 | path: `/ping`, 34 | method: "GET", 35 | secure: true, 36 | format: "json", 37 | ...params, 38 | }); 39 | /** 40 | * No description 41 | * 42 | * @tags Ping 43 | * @name HeadPing 44 | * @request HEAD:/ping 45 | * @secure 46 | */ 47 | headPing = (params: RequestParams = {}) => 48 | this.http.request({ 49 | path: `/ping`, 50 | method: "HEAD", 51 | secure: true, 52 | format: "json", 53 | ...params, 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /src/__generated__/lidarr/Feed.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Feed { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags CalendarFeed 26 | * @name V1CalendarLidarrIcsList 27 | * @request GET:/feed/v1/calendar/lidarr.ics 28 | * @secure 29 | */ 30 | v1CalendarLidarrIcsList = ( 31 | query?: { 32 | /** 33 | * @format int32 34 | * @default 7 35 | */ 36 | pastDays?: number; 37 | /** 38 | * @format int32 39 | * @default 28 40 | */ 41 | futureDays?: number; 42 | /** @default "" */ 43 | tags?: string; 44 | /** @default false */ 45 | unmonitored?: boolean; 46 | }, 47 | params: RequestParams = {}, 48 | ) => 49 | this.http.request({ 50 | path: `/feed/v1/calendar/lidarr.ics`, 51 | method: "GET", 52 | query: query, 53 | secure: true, 54 | ...params, 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /src/__generated__/readarr/Feed.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Feed { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags CalendarFeed 26 | * @name V1CalendarReadarrIcsList 27 | * @request GET:/feed/v1/calendar/readarr.ics 28 | * @secure 29 | */ 30 | v1CalendarReadarrIcsList = ( 31 | query?: { 32 | /** 33 | * @format int32 34 | * @default 7 35 | */ 36 | pastDays?: number; 37 | /** 38 | * @format int32 39 | * @default 28 40 | */ 41 | futureDays?: number; 42 | /** @default "" */ 43 | tagList?: string; 44 | /** @default false */ 45 | unmonitored?: boolean; 46 | }, 47 | params: RequestParams = {}, 48 | ) => 49 | this.http.request({ 50 | path: `/feed/v1/calendar/readarr.ics`, 51 | method: "GET", 52 | query: query, 53 | secure: true, 54 | ...params, 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /pkgs/nix/package.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | pkgs, 4 | ... 5 | }: 6 | pkgs.stdenvNoCC.mkDerivation (finalAttrs: { 7 | buildPhase = '' 8 | runHook preBuild 9 | pnpm build 10 | runHook postBuild 11 | ''; 12 | 13 | checkPhase = '' 14 | runHook preCheck 15 | pnpm test 16 | runHook postCheck 17 | ''; 18 | 19 | CI = "true"; 20 | 21 | installPhase = '' 22 | runHook preInstall 23 | install -Dm644 -t $out/share bundle.cjs 24 | makeWrapper ${lib.getExe pkgs.nodejs_24} $out/bin/configarr \ 25 | --add-flags "$out/share/bundle.cjs" 26 | runHook postInstall 27 | ''; 28 | 29 | meta = { 30 | changelog = "https://github.com/raydak-labs/configarr/blob/${finalAttrs.src.rev}/CHANGELOG.md"; 31 | description = "Sync TRaSH Guides + custom configs with Sonarr/Radarr"; 32 | homepage = "https://github.com/raydak-labs/configarr"; 33 | license = lib.licenses.agpl3Only; 34 | mainProgram = "configarr"; 35 | maintainers = with lib.maintainers; [lord-valen]; 36 | platforms = lib.platforms.all; 37 | }; 38 | 39 | nativeBuildInputs = [ 40 | pkgs.makeBinaryWrapper 41 | pkgs.nodejs_24 42 | pkgs.pnpm.configHook 43 | ]; 44 | 45 | pname = "configarr"; 46 | 47 | pnpmDeps = pkgs.pnpm.fetchDeps { 48 | fetcherVersion = 1; 49 | hash = "sha256-0P5gT29uLCmm10Xerk9ZVblEoauTEd9jzi0jseO3Ojc="; 50 | inherit (finalAttrs) pname src version; 51 | }; 52 | 53 | src = pkgs.fetchFromGitHub { 54 | owner = "raydak-labs"; 55 | repo = "configarr"; 56 | rev = "v${finalAttrs.version}"; 57 | hash = "sha256-fgv6wiK5wh0jAczJWy3Iqs3OK81ckNr3bOZD32bTCQQ="; 58 | }; 59 | 60 | version = "1.17.2"; 61 | }) 62 | -------------------------------------------------------------------------------- /src/__generated__/radarr/Feed.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | import { CalendarReleaseType } from "./data-contracts"; 15 | 16 | export class Feed { 17 | http: HttpClient; 18 | 19 | constructor(http: HttpClient) { 20 | this.http = http; 21 | } 22 | 23 | /** 24 | * No description 25 | * 26 | * @tags CalendarFeed 27 | * @name V3CalendarRadarrIcsList 28 | * @request GET:/feed/v3/calendar/radarr.ics 29 | * @secure 30 | */ 31 | v3CalendarRadarrIcsList = ( 32 | query?: { 33 | /** 34 | * @format int32 35 | * @default 7 36 | */ 37 | pastDays?: number; 38 | /** 39 | * @format int32 40 | * @default 28 41 | */ 42 | futureDays?: number; 43 | /** @default "" */ 44 | tags?: string; 45 | /** @default false */ 46 | unmonitored?: boolean; 47 | releaseTypes?: CalendarReleaseType[]; 48 | }, 49 | params: RequestParams = {}, 50 | ) => 51 | this.http.request({ 52 | path: `/feed/v3/calendar/radarr.ics`, 53 | method: "GET", 54 | query: query, 55 | secure: true, 56 | ...params, 57 | }); 58 | } 59 | -------------------------------------------------------------------------------- /examples/full/templates/radarr-cf.yml: -------------------------------------------------------------------------------- 1 | custom_formats: 2 | - trash_ids: 3 | # Can be copied from recyclarr 4 | 5 | # HQ Release Groups 6 | - 3a3ff47579026e76d6504ebea39390de # Remux Tier 01 7 | - 9f98181fe5a3fbeb0cc29340da2a468a # Remux Tier 02 8 | - 8baaf0b3142bf4d94c42a724f034e27a # Remux Tier 03 9 | - c20f169ef63c5f40c2def54abaf4438e # WEB Tier 01 10 | - 403816d65392c79236dcb6dd591aeda4 # WEB Tier 02 11 | - af94e0fe497124d1f9ce732069ec8c3b # WEB Tier 03 12 | assign_scores_to: 13 | - name: ExampleProfile 14 | 15 | - trash_ids: 16 | # Streaming Services 17 | - b3b3a6ac74ecbd56bcdbefa4799fb9df # AMZN 18 | - 40e9380490e748672c2522eaaeb692f7 # ATVP 19 | - f6ff65b3f4b464a79dcc75950fe20382 # CRAV 20 | - 84272245b2988854bfb76a16e60baea5 # DSNP 21 | - 917d1f2c845b2b466036b0cc2d7c72a3 # FOD 22 | - 509e5f41146e278f9eab1ddaceb34515 # HBO 23 | - 5763d1b0ce84aff3b21038eea8e9b8ad # HMAX 24 | - 526d445d4c16214309f0fd2b3be18a89 # Hulu 25 | - 6185878161f1e2eef9cd0641a0d09eae # iP 26 | - 6a061313d22e51e0f25b7cd4dc065233 # MAX 27 | - 170b1d363bd8516fbf3a3eb05d4faff6 # NF 28 | - fbca986396c5e695ef7b2def3c755d01 # OViD 29 | - bf7e73dd1d85b12cc527dc619761c840 # Pathe 30 | - c9fd353f8f5f1baf56dc601c4cb29920 # PCOK 31 | - e36a0ba1bc902b26ee40818a1d59b8bd # PMTP 32 | - c2863d2a50c9acad1fb50e53ece60817 # STAN 33 | - f1b0bae9bc222dab32c1b38b5a7a1088 # TVer 34 | - 279bda7434fd9075786de274e6c3c202 # U-NEXT 35 | assign_scores_to: 36 | - name: ExampleProfile 37 | 38 | - trash_ids: 39 | - custom-size-more-40gb # custom CF 40 | assign_scores_to: 41 | - name: ExampleProfile 42 | score: -10000 43 | -------------------------------------------------------------------------------- /src/__generated__/sonarr/Feed.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Feed { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags CalendarFeed 26 | * @name V3CalendarSonarrIcsList 27 | * @request GET:/feed/v3/calendar/sonarr.ics 28 | * @secure 29 | */ 30 | v3CalendarSonarrIcsList = ( 31 | query?: { 32 | /** 33 | * @format int32 34 | * @default 7 35 | */ 36 | pastDays?: number; 37 | /** 38 | * @format int32 39 | * @default 28 40 | */ 41 | futureDays?: number; 42 | /** @default "" */ 43 | tags?: string; 44 | /** @default false */ 45 | unmonitored?: boolean; 46 | /** @default false */ 47 | premieresOnly?: boolean; 48 | /** @default false */ 49 | asAllDay?: boolean; 50 | }, 51 | params: RequestParams = {}, 52 | ) => 53 | this.http.request({ 54 | path: `/feed/v3/calendar/sonarr.ics`, 55 | method: "GET", 56 | query: query, 57 | secure: true, 58 | ...params, 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /src/__generated__/whisparr/Feed.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Feed { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags CalendarFeed 26 | * @name V3CalendarWhisparrIcsList 27 | * @request GET:/feed/v3/calendar/whisparr.ics 28 | * @secure 29 | */ 30 | v3CalendarWhisparrIcsList = ( 31 | query?: { 32 | /** 33 | * @format int32 34 | * @default 7 35 | */ 36 | pastDays?: number; 37 | /** 38 | * @format int32 39 | * @default 28 40 | */ 41 | futureDays?: number; 42 | /** @default "" */ 43 | tags?: string; 44 | /** @default false */ 45 | unmonitored?: boolean; 46 | /** @default false */ 47 | premieresOnly?: boolean; 48 | /** @default false */ 49 | asAllDay?: boolean; 50 | }, 51 | params: RequestParams = {}, 52 | ) => 53 | this.http.request({ 54 | path: `/feed/v3/calendar/whisparr.ics`, 55 | method: "GET", 56 | query: query, 57 | secure: true, 58 | ...params, 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md 2 | # TODO because multiarch build has problems with QEMU and Node we cannot use alpine here: https://github.com/nodejs/docker-node/issues/1798 3 | FROM node:24.11.1-slim AS base 4 | WORKDIR /app 5 | 6 | ENV PNPM_HOME="/opt/pnpm" 7 | ENV COREPACK_HOME="/opt/corepack" 8 | ENV PATH="$PNPM_HOME:$PATH" 9 | ENV CI=true 10 | 11 | RUN apt-get update && apt-get install -y \ 12 | git \ 13 | && rm -rf /var/lib/apt/lists/* 14 | 15 | COPY package.json pnpm-lock.yaml ./ 16 | 17 | # Do it here to add the packageManager field to the package.json 18 | RUN corepack enable \ 19 | && corepack prepare pnpm@10 --activate \ 20 | && corepack use pnpm@10 21 | 22 | RUN pnpm config set store-dir ~/.pnpm-store 23 | 24 | RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm install --frozen-lockfile 25 | 26 | FROM base AS builder 27 | COPY src src/ 28 | COPY esbuild.ts ./ 29 | 30 | RUN pnpm run build 31 | 32 | FROM base AS dev 33 | # manually mount src etc 34 | 35 | ARG CONFIGARR_VERSION=dev 36 | ENV CONFIGARR_VERSION=${CONFIGARR_VERSION} 37 | 38 | CMD [ "pnpm", "start" ] 39 | # https://github.com/evanw/esbuild/issues/1921 40 | FROM node:24.11.1-alpine AS prod 41 | WORKDIR /app 42 | 43 | RUN apk add --no-cache libstdc++ dumb-init git 44 | 45 | # Allow global git access independent of container user and directory user. See #240, #241 46 | RUN git config --global --add safe.directory '*' 47 | 48 | # TODO maybe in future. Results in breaking change 49 | #USER node 50 | 51 | COPY --from=builder /app/bundle.cjs /app/index.js 52 | 53 | ARG CONFIGARR_VERSION=dev 54 | ENV CONFIGARR_VERSION=${CONFIGARR_VERSION} 55 | # Run with dumb-init to not start node with PID=1, since Node.js was not designed to run as PID 1 56 | CMD ["dumb-init", "node", "index.js"] 57 | -------------------------------------------------------------------------------- /src/__generated__/lidarr/Login.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { ContentType, HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Login { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags Authentication 26 | * @name LoginCreate 27 | * @request POST:/login 28 | * @secure 29 | */ 30 | loginCreate = ( 31 | data: { 32 | username?: string; 33 | password?: string; 34 | rememberMe?: string; 35 | }, 36 | query?: { 37 | returnUrl?: string; 38 | }, 39 | params: RequestParams = {}, 40 | ) => 41 | this.http.request({ 42 | path: `/login`, 43 | method: "POST", 44 | query: query, 45 | body: data, 46 | secure: true, 47 | type: ContentType.FormData, 48 | ...params, 49 | }); 50 | /** 51 | * No description 52 | * 53 | * @tags StaticResource 54 | * @name LoginList 55 | * @request GET:/login 56 | * @secure 57 | */ 58 | loginList = (params: RequestParams = {}) => 59 | this.http.request({ 60 | path: `/login`, 61 | method: "GET", 62 | secure: true, 63 | ...params, 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /src/__generated__/radarr/Login.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { ContentType, HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Login { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags Authentication 26 | * @name LoginCreate 27 | * @request POST:/login 28 | * @secure 29 | */ 30 | loginCreate = ( 31 | data: { 32 | username?: string; 33 | password?: string; 34 | rememberMe?: string; 35 | }, 36 | query?: { 37 | returnUrl?: string; 38 | }, 39 | params: RequestParams = {}, 40 | ) => 41 | this.http.request({ 42 | path: `/login`, 43 | method: "POST", 44 | query: query, 45 | body: data, 46 | secure: true, 47 | type: ContentType.FormData, 48 | ...params, 49 | }); 50 | /** 51 | * No description 52 | * 53 | * @tags StaticResource 54 | * @name LoginList 55 | * @request GET:/login 56 | * @secure 57 | */ 58 | loginList = (params: RequestParams = {}) => 59 | this.http.request({ 60 | path: `/login`, 61 | method: "GET", 62 | secure: true, 63 | ...params, 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /src/__generated__/readarr/Login.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { ContentType, HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Login { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags Authentication 26 | * @name LoginCreate 27 | * @request POST:/login 28 | * @secure 29 | */ 30 | loginCreate = ( 31 | data: { 32 | username?: string; 33 | password?: string; 34 | rememberMe?: string; 35 | }, 36 | query?: { 37 | returnUrl?: string; 38 | }, 39 | params: RequestParams = {}, 40 | ) => 41 | this.http.request({ 42 | path: `/login`, 43 | method: "POST", 44 | query: query, 45 | body: data, 46 | secure: true, 47 | type: ContentType.FormData, 48 | ...params, 49 | }); 50 | /** 51 | * No description 52 | * 53 | * @tags StaticResource 54 | * @name LoginList 55 | * @request GET:/login 56 | * @secure 57 | */ 58 | loginList = (params: RequestParams = {}) => 59 | this.http.request({ 60 | path: `/login`, 61 | method: "GET", 62 | secure: true, 63 | ...params, 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /src/__generated__/sonarr/Login.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { ContentType, HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Login { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags Authentication 26 | * @name LoginCreate 27 | * @request POST:/login 28 | * @secure 29 | */ 30 | loginCreate = ( 31 | data: { 32 | username?: string; 33 | password?: string; 34 | rememberMe?: string; 35 | }, 36 | query?: { 37 | returnUrl?: string; 38 | }, 39 | params: RequestParams = {}, 40 | ) => 41 | this.http.request({ 42 | path: `/login`, 43 | method: "POST", 44 | query: query, 45 | body: data, 46 | secure: true, 47 | type: ContentType.FormData, 48 | ...params, 49 | }); 50 | /** 51 | * No description 52 | * 53 | * @tags StaticResource 54 | * @name LoginList 55 | * @request GET:/login 56 | * @secure 57 | */ 58 | loginList = (params: RequestParams = {}) => 59 | this.http.request({ 60 | path: `/login`, 61 | method: "GET", 62 | secure: true, 63 | ...params, 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /src/__generated__/whisparr/Login.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-nocheck 4 | /* 5 | * --------------------------------------------------------------- 6 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 7 | * ## ## 8 | * ## AUTHOR: acacode ## 9 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 10 | * --------------------------------------------------------------- 11 | */ 12 | 13 | import { ContentType, HttpClient, RequestParams } from "./../ky-client"; 14 | 15 | export class Login { 16 | http: HttpClient; 17 | 18 | constructor(http: HttpClient) { 19 | this.http = http; 20 | } 21 | 22 | /** 23 | * No description 24 | * 25 | * @tags Authentication 26 | * @name LoginCreate 27 | * @request POST:/login 28 | * @secure 29 | */ 30 | loginCreate = ( 31 | data: { 32 | username?: string; 33 | password?: string; 34 | rememberMe?: string; 35 | }, 36 | query?: { 37 | returnUrl?: string; 38 | }, 39 | params: RequestParams = {}, 40 | ) => 41 | this.http.request({ 42 | path: `/login`, 43 | method: "POST", 44 | query: query, 45 | body: data, 46 | secure: true, 47 | type: ContentType.FormData, 48 | ...params, 49 | }); 50 | /** 51 | * No description 52 | * 53 | * @tags StaticResource 54 | * @name LoginList 55 | * @request GET:/login 56 | * @secure 57 | */ 58 | loginList = (params: RequestParams = {}) => 59 | this.http.request({ 60 | path: `/login`, 61 | method: "GET", 62 | secure: true, 63 | ...params, 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/doc-preview.yml: -------------------------------------------------------------------------------- 1 | name: Documentation (Preview) 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | paths: 7 | - docs/** 8 | workflow_dispatch: 9 | 10 | jobs: 11 | deploy: 12 | runs-on: ubuntu-latest 13 | name: Deploy 14 | permissions: 15 | contents: read 16 | deployments: write 17 | pull-requests: write 18 | steps: 19 | - uses: actions/checkout@v6 20 | with: 21 | fetch-depth: 0 22 | 23 | - name: Inject enhanced GitHub environment variables 24 | uses: rlespinasse/github-slug-action@v5 25 | 26 | - uses: pnpm/action-setup@v4 27 | with: 28 | version: latest 29 | 30 | - uses: actions/setup-node@v6 31 | with: 32 | node-version: lts/* 33 | cache: pnpm 34 | 35 | - name: Install & build 36 | working-directory: docs 37 | run: | 38 | pnpm i 39 | pnpm build 40 | 41 | - name: Deploy 42 | uses: cloudflare/wrangler-action@v3 43 | id: cloudflare-deploy 44 | with: 45 | apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} 46 | accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} 47 | command: pages deploy ./docs/build --project-name=configarr-preview --branch=${{ env.GITHUB_REF_SLUG_URL }} 48 | # Optional: Enable this if you want to have GitHub Deployments triggered 49 | #gitHubToken: ${{ secrets.GITHUB_TOKEN }} 50 | 51 | - name: Comment PR 52 | uses: thollander/actions-comment-pull-request@v3 53 | if: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'main' }} 54 | with: 55 | message: | 56 | Doc preview deployed to: ${{ steps.cloudflare-deploy.outputs.deployment-url }} 57 | comment-tag: docs-preview 58 | -------------------------------------------------------------------------------- /custom/cfs/de-tier-03.json: -------------------------------------------------------------------------------- 1 | { 2 | "trash_id": "de-tier-03-version1", 3 | "trash_scores": {}, 4 | "name": "GER HD Rel. Group Tier 03", 5 | "includeCustomFormatWhenRenaming": false, 6 | "specifications": [ 7 | { 8 | "name": "AIDA", 9 | "implementation": "ReleaseGroupSpecification", 10 | "negate": false, 11 | "required": false, 12 | "fields": { 13 | "value": "AIDA" 14 | } 15 | }, 16 | { 17 | "name": "SunDry", 18 | "implementation": "ReleaseGroupSpecification", 19 | "negate": false, 20 | "required": false, 21 | "fields": { 22 | "value": "SunDry" 23 | } 24 | }, 25 | { 26 | "name": "TVP (Inactive)", 27 | "implementation": "ReleaseGroupSpecification", 28 | "negate": false, 29 | "required": false, 30 | "fields": { 31 | "value": "TVP" 32 | } 33 | }, 34 | { 35 | "name": "UTOPiA", 36 | "implementation": "ReleaseGroupSpecification", 37 | "negate": false, 38 | "required": false, 39 | "fields": { 40 | "value": "UTOPiA" 41 | } 42 | }, 43 | { 44 | "name": "VECTOR (Evaluate 07.2024)", 45 | "implementation": "ReleaseGroupSpecification", 46 | "negate": false, 47 | "required": false, 48 | "fields": { 49 | "value": "VECTOR" 50 | } 51 | }, 52 | { 53 | "name": "EXCiTED (Inactive)", 54 | "implementation": "ReleaseGroupSpecification", 55 | "negate": false, 56 | "required": false, 57 | "fields": { 58 | "value": "EXCiTED" 59 | } 60 | }, 61 | { 62 | "name": "FRAGGERS (Inactive)", 63 | "implementation": "ReleaseGroupSpecification", 64 | "negate": false, 65 | "required": false, 66 | "fields": { 67 | "value": "FRAGGERS" 68 | } 69 | } 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | paths: 8 | - docs/** 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | name: Build 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v6 17 | with: 18 | fetch-depth: 0 19 | 20 | - uses: pnpm/action-setup@v4 21 | with: 22 | version: latest 23 | 24 | - uses: actions/setup-node@v6 25 | with: 26 | node-version: lts/* 27 | cache: pnpm 28 | 29 | - name: Install & build 30 | working-directory: docs 31 | run: | 32 | pnpm i 33 | pnpm build 34 | 35 | - name: Upload static files as artifact 36 | id: deployment 37 | uses: actions/upload-pages-artifact@v4 # or specific "vX.X.X" version tag for this action 38 | with: 39 | path: ./docs/build 40 | 41 | # Deploy job 42 | deploy: 43 | if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' 44 | 45 | # Add a dependency to the build job 46 | needs: build 47 | 48 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment 49 | permissions: 50 | pages: write # to deploy to Pages 51 | id-token: write # to verify the deployment originates from an appropriate source 52 | 53 | # Deploy to the github-pages environment 54 | environment: 55 | name: github-pages 56 | url: ${{ steps.deployment.outputs.page_url }} 57 | 58 | # Specify runner + deployment step 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Deploy to GitHub Pages 62 | id: deployment 63 | uses: actions/deploy-pages@v4 # or specific "vX.X.X" version tag for this action 64 | -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | import { levels, pino } from "pino"; 2 | import pretty from "pino-pretty"; 3 | import { getEnvs, getHelpers } from "./env"; 4 | 5 | const maxArrayLength = Object.values(levels.labels).reduce((maxLength, currentArray) => { 6 | return Math.max(maxLength, currentArray.length); 7 | }, 0); 8 | 9 | // Function to format a level string by appending spaces 10 | const neededSpaces = (level: string): string => { 11 | const spacesNeeded = maxArrayLength - level.length; 12 | const spaces = " ".repeat(spacesNeeded > 0 ? spacesNeeded : 0); 13 | return `${spaces}`; 14 | }; 15 | 16 | const transformedLevelMap = Object.entries(levels.values).reduce((p, [key, value]) => { 17 | p.set(key.toUpperCase(), neededSpaces(key)); 18 | 19 | return p; 20 | }, new Map()); 21 | 22 | const stream = pretty({ 23 | levelFirst: true, 24 | ignore: "hostname,pid", 25 | // @ts-ignore Temporary weird error 26 | customPrettifiers: { 27 | level: (level, key, log, { colors, label, labelColorized }) => `${labelColorized}${transformedLevelMap.get(label)}`, 28 | }, 29 | }); 30 | 31 | export const logger = pino( 32 | { 33 | level: getEnvs().LOG_LEVEL, 34 | // transport: { 35 | // target: "pino-pretty", 36 | // options: { 37 | // colorize: true, 38 | // }, 39 | // }, 40 | }, 41 | stream, 42 | ); 43 | 44 | export const logSeparator = () => { 45 | logger.info(`#############################################`); 46 | }; 47 | 48 | export const logHeading = (title: string) => { 49 | logger.info(""); 50 | logSeparator(); 51 | logger.info(`### ${title}`); 52 | logSeparator(); 53 | logger.info(""); 54 | }; 55 | 56 | export const logInstanceHeading = (title: string) => { 57 | logger.info(`### ${title}`); 58 | }; 59 | 60 | // For debugging how envs have been loaded 61 | logger.debug({ envs: getEnvs(), helpers: getHelpers() }, `Loaded following configuration from ENVs and mapped`); 62 | -------------------------------------------------------------------------------- /docs/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from "@docusaurus/Link"; 2 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 3 | import HomepageFeatures from "@site/src/components/HomepageFeatures"; 4 | import Heading from "@theme/Heading"; 5 | import Layout from "@theme/Layout"; 6 | import clsx from "clsx"; 7 | 8 | import Head from "@docusaurus/Head"; 9 | import styles from "./index.module.css"; 10 | import useBaseUrl from "@docusaurus/useBaseUrl"; 11 | 12 | function HomepageHeader() { 13 | const { siteConfig } = useDocusaurusContext(); 14 | return ( 15 |
16 |
17 | Configarr Logo 18 | 19 | {siteConfig.title} 20 | 21 |

{siteConfig.tagline}

22 |
23 | 24 | Get Started ! 25 | 26 | 27 | 28 |