diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13275b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.terraform/ +*.tfstate +*.tfstate.backup +*.tfplan +backend.tf +.terragrunt-cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..646cd65 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,24 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: end-of-file-fixer + types: [yaml] + - id: trailing-whitespace + types: [yaml] + - repo: https://github.com/gruntwork-io/pre-commit + rev: v0.1.30 + hooks: + - id: tofu-fmt + - id: tofu-validate + - id: tflint + - id: terragrunt-hcl-fmt + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.37.1 + hooks: + - id: yamllint + args: + [ + "-d {extends: relaxed, rules: {line-length: disable}, ignore: chart}", + "-s", + ] diff --git a/.woodpecker/apply.yaml b/.woodpecker/apply.yaml new file mode 100644 index 0000000..84a79e2 --- /dev/null +++ b/.woodpecker/apply.yaml @@ -0,0 +1,23 @@ +when: + - event: push + branch: main + +steps: + - name: apply + image: git.unkin.net/unkin/almalinux9-opentofu:20260606 + environment: + VAULT_AUTH_METHOD: kubernetes + commands: + - dnf install vault -y + - make plan + - make apply + backend_options: + kubernetes: + serviceAccountName: terraform-sonarr + resources: + requests: + memory: 512Mi + cpu: 1 + limits: + memory: 2Gi + cpu: 2 diff --git a/.woodpecker/plan.yaml b/.woodpecker/plan.yaml new file mode 100644 index 0000000..32c6281 --- /dev/null +++ b/.woodpecker/plan.yaml @@ -0,0 +1,21 @@ +when: + - event: pull_request + +steps: + - name: plan + image: git.unkin.net/unkin/almalinux9-opentofu:20260606 + environment: + VAULT_AUTH_METHOD: kubernetes + commands: + - dnf install vault -y + - make plan + backend_options: + kubernetes: + serviceAccountName: terraform-sonarr + resources: + requests: + memory: 512Mi + cpu: 1 + limits: + memory: 2Gi + cpu: 2 diff --git a/.woodpecker/pre-commit.yaml b/.woodpecker/pre-commit.yaml new file mode 100644 index 0000000..5c5738f --- /dev/null +++ b/.woodpecker/pre-commit.yaml @@ -0,0 +1,18 @@ +when: + - event: pull_request + +steps: + - name: pre-commit + image: git.unkin.net/unkin/almalinux9-opentofu:20260606 + commands: + - uvx pre-commit run --all-files + backend_options: + kubernetes: + serviceAccountName: default + resources: + requests: + memory: 512Mi + cpu: 1 + limits: + memory: 2Gi + cpu: 2 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f879029 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +.PHONY: init plan apply format + +VAULT_AUTH_METHOD ?= approle +VAULT_K8S_ROLE ?= woodpecker_terraform_sonarr +VAULT_K8S_MOUNT ?= auth/k8s/au/syd1 +VAULT_K8S_JWT_PATH ?= /var/run/secrets/kubernetes.io/serviceaccount/token + +define vault_env + @export VAULT_ADDR="https://vault.service.consul:8200" && \ + if [ "$(VAULT_AUTH_METHOD)" = "kubernetes" ]; then \ + export VAULT_TOKEN=$$(vault write -field=token $(VAULT_K8S_MOUNT)/login role=$(VAULT_K8S_ROLE) jwt=$$(cat $(VAULT_K8S_JWT_PATH))); \ + else \ + export VAULT_TOKEN=$$(vault write -field=token auth/approle/login role_id=$$VAULT_ROLEID); \ + fi && \ + export CONSUL_HTTP_TOKEN=$$(vault read -field=token consul_root/au/syd1/creds/terraform-sonarr) && \ + export SONARR_API_KEY=$$(vault kv get -field=apitoken kv/service/media-apps/sonarr) +endef + +init: + @$(call vault_env) && \ + terragrunt run --all --non-interactive init -- -upgrade + +plan: init + @$(call vault_env) && \ + terragrunt run --all --parallelism 4 --non-interactive plan + +apply: init + @$(call vault_env) && \ + terragrunt run --all --parallelism 2 --non-interactive apply + +format: + @echo "Formatting OpenTofu files..." + @tofu fmt -recursive . + @echo "Formatting Terragrunt files..." + @terragrunt hcl fmt diff --git a/config/config.hcl b/config/config.hcl new file mode 100644 index 0000000..d112876 --- /dev/null +++ b/config/config.hcl @@ -0,0 +1,46 @@ +locals { + config_files = fileset(".", "**/*.yaml") + + all_configs = { + for file_path in local.config_files : + file_path => yamldecode(file(file_path)) + } + + config = { + custom_formats = { + for file_path, content in local.all_configs : + trimsuffix(basename(file_path), ".yaml") => content + if startswith(file_path, "custom_format/") + } + quality_profiles = { + for file_path, content in local.all_configs : + trimsuffix(basename(file_path), ".yaml") => content + if startswith(file_path, "quality_profile/") + } + download_clients = { + for file_path, content in local.all_configs : + trimsuffix(basename(file_path), ".yaml") => content + if startswith(file_path, "download_client/") + } + indexers = { + for file_path, content in local.all_configs : + trimsuffix(basename(file_path), ".yaml") => content + if startswith(file_path, "indexer/") + } + notifications = { + for file_path, content in local.all_configs : + trimsuffix(basename(file_path), ".yaml") => content + if startswith(file_path, "notification/") + } + delay_profiles = { + for file_path, content in local.all_configs : + trimsuffix(basename(file_path), ".yaml") => content + if startswith(file_path, "delay_profile/") + } + root_folders = { + for file_path, content in local.all_configs : + trimsuffix(basename(file_path), ".yaml") => content + if startswith(file_path, "root_folder/") + } + } +} diff --git a/config/custom_format/format_av1.yaml b/config/custom_format/format_av1.yaml new file mode 100644 index 0000000..7d146e2 --- /dev/null +++ b/config/custom_format/format_av1.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: av1 + implementation: ReleaseTitleSpecification + negate: false + required: false + fields: + - name: value + value: "av1" diff --git a/config/custom_format/format_x264.yaml b/config/custom_format/format_x264.yaml new file mode 100644 index 0000000..b16d308 --- /dev/null +++ b/config/custom_format/format_x264.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: x264 + implementation: ReleaseTitleSpecification + negate: false + required: false + fields: + - name: value + value: "(x|h)\\.?264" diff --git a/config/custom_format/format_x265.yaml b/config/custom_format/format_x265.yaml new file mode 100644 index 0000000..bb0e8b5 --- /dev/null +++ b/config/custom_format/format_x265.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: x265 + implementation: ReleaseTitleSpecification + negate: false + required: false + fields: + - name: value + value: "(((x|h)\\.?265)|(HEVC))" diff --git a/config/custom_format/hdr10.yaml b/config/custom_format/hdr10.yaml new file mode 100644 index 0000000..be31992 --- /dev/null +++ b/config/custom_format/hdr10.yaml @@ -0,0 +1,23 @@ +include_custom_format_when_renaming: false +specifications: + - name: hdr10 + implementation: ReleaseTitleSpecification + negate: false + required: true + fields: + - name: value + value: "hdr10" + - name: x265 + implementation: ReleaseTitleSpecification + negate: false + required: true + fields: + - name: value + value: "(((x|h)\\.?265)|(HEVC))" + - name: Surround Sound + implementation: ReleaseTitleSpecification + negate: false + required: true + fields: + - name: value + value: "DTS.?(HD|ES|X(?!\\D))|TRUEHD|ATMOS|DD(\\+|P).?([5-9])|EAC3.?([5-9])" diff --git a/config/custom_format/hvec_10bit.yaml b/config/custom_format/hvec_10bit.yaml new file mode 100644 index 0000000..fc4a7c3 --- /dev/null +++ b/config/custom_format/hvec_10bit.yaml @@ -0,0 +1,30 @@ +include_custom_format_when_renaming: false +specifications: + - name: 10bit + implementation: ReleaseTitleSpecification + negate: false + required: true + fields: + - name: value + value: "10bit" + - name: hvec + implementation: ReleaseTitleSpecification + negate: false + required: false + fields: + - name: value + value: "hvec" + - name: x265 + implementation: ReleaseTitleSpecification + negate: false + required: true + fields: + - name: value + value: "(((x|h)\\.?265)|(HEVC))" + - name: "release_iVy: iVy" + implementation: ReleaseGroupSpecification + negate: false + required: true + fields: + - name: value + value: "-iVy$" diff --git a/config/custom_format/release_AsmoFuscated.yaml b/config/custom_format/release_AsmoFuscated.yaml new file mode 100644 index 0000000..f2a6c3d --- /dev/null +++ b/config/custom_format/release_AsmoFuscated.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: "AsmoFuscated " + implementation: ReleaseGroupSpecification + negate: false + required: false + fields: + - name: value + value: "AsmoFuscated$" diff --git a/config/custom_format/release_d3g.yaml b/config/custom_format/release_d3g.yaml new file mode 100644 index 0000000..a9c9c69 --- /dev/null +++ b/config/custom_format/release_d3g.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: d3g + implementation: ReleaseGroupSpecification + negate: false + required: false + fields: + - name: value + value: "d3g$" diff --git a/config/custom_format/release_iVy.yaml b/config/custom_format/release_iVy.yaml new file mode 100644 index 0000000..767f70b --- /dev/null +++ b/config/custom_format/release_iVy.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: iVy + implementation: ReleaseGroupSpecification + negate: false + required: false + fields: + - name: value + value: "iVy$" diff --git a/config/custom_format/resolution_1080p.yaml b/config/custom_format/resolution_1080p.yaml new file mode 100644 index 0000000..b0343e5 --- /dev/null +++ b/config/custom_format/resolution_1080p.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: 1080p + implementation: ResolutionSpecification + negate: false + required: false + fields: + - name: value + value: "1080" diff --git a/config/custom_format/resolution_2160p.yaml b/config/custom_format/resolution_2160p.yaml new file mode 100644 index 0000000..c554727 --- /dev/null +++ b/config/custom_format/resolution_2160p.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: 2160p + implementation: ResolutionSpecification + negate: false + required: false + fields: + - name: value + value: "2160" diff --git a/config/custom_format/resolution_720p.yaml b/config/custom_format/resolution_720p.yaml new file mode 100644 index 0000000..3661b07 --- /dev/null +++ b/config/custom_format/resolution_720p.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: 720p + implementation: ResolutionSpecification + negate: false + required: false + fields: + - name: value + value: "720" diff --git a/config/custom_format/size_0_800.yaml b/config/custom_format/size_0_800.yaml new file mode 100644 index 0000000..ffb1a25 --- /dev/null +++ b/config/custom_format/size_0_800.yaml @@ -0,0 +1,11 @@ +include_custom_format_when_renaming: false +specifications: + - name: size_0_800 + implementation: SizeSpecification + negate: false + required: false + fields: + - name: min + value: "0" + - name: max + value: "0.8" diff --git a/config/custom_format/size_1500_3000.yaml b/config/custom_format/size_1500_3000.yaml new file mode 100644 index 0000000..2b56a5e --- /dev/null +++ b/config/custom_format/size_1500_3000.yaml @@ -0,0 +1,11 @@ +include_custom_format_when_renaming: false +specifications: + - name: size_1500_3000 + implementation: SizeSpecification + negate: false + required: false + fields: + - name: min + value: "1.5" + - name: max + value: "3" diff --git a/config/custom_format/size_3000_6000.yaml b/config/custom_format/size_3000_6000.yaml new file mode 100644 index 0000000..ad1c052 --- /dev/null +++ b/config/custom_format/size_3000_6000.yaml @@ -0,0 +1,11 @@ +include_custom_format_when_renaming: false +specifications: + - name: size_3000_6000 + implementation: SizeSpecification + negate: false + required: false + fields: + - name: min + value: "3" + - name: max + value: "6" diff --git a/config/custom_format/size_6000_10000.yaml b/config/custom_format/size_6000_10000.yaml new file mode 100644 index 0000000..d1c673e --- /dev/null +++ b/config/custom_format/size_6000_10000.yaml @@ -0,0 +1,11 @@ +include_custom_format_when_renaming: false +specifications: + - name: size_6000_10000 + implementation: SizeSpecification + negate: false + required: false + fields: + - name: min + value: "6" + - name: max + value: "10" diff --git a/config/custom_format/size_800_1500.yaml b/config/custom_format/size_800_1500.yaml new file mode 100644 index 0000000..e852f31 --- /dev/null +++ b/config/custom_format/size_800_1500.yaml @@ -0,0 +1,11 @@ +include_custom_format_when_renaming: false +specifications: + - name: size_800_1500 + implementation: SizeSpecification + negate: false + required: false + fields: + - name: min + value: "0.8" + - name: max + value: "1.5" diff --git a/config/custom_format/source_bluray.yaml b/config/custom_format/source_bluray.yaml new file mode 100644 index 0000000..a2814af --- /dev/null +++ b/config/custom_format/source_bluray.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: bluray + implementation: SourceSpecification + negate: false + required: false + fields: + - name: value + value: "6" diff --git a/config/custom_format/source_hdtv.yaml b/config/custom_format/source_hdtv.yaml new file mode 100644 index 0000000..b402341 --- /dev/null +++ b/config/custom_format/source_hdtv.yaml @@ -0,0 +1,9 @@ +include_custom_format_when_renaming: false +specifications: + - name: hdtv + implementation: SourceSpecification + negate: false + required: false + fields: + - name: value + value: "1" diff --git a/config/custom_format/source_webdl.yaml b/config/custom_format/source_webdl.yaml new file mode 100644 index 0000000..f95ff1a --- /dev/null +++ b/config/custom_format/source_webdl.yaml @@ -0,0 +1,16 @@ +include_custom_format_when_renaming: false +specifications: + - name: webdl + implementation: SourceSpecification + negate: false + required: false + fields: + - name: value + value: "3" + - name: webrip + implementation: SourceSpecification + negate: false + required: false + fields: + - name: value + value: "4" diff --git a/config/delay_profile/default.yaml b/config/delay_profile/default.yaml new file mode 100644 index 0000000..e4256b6 --- /dev/null +++ b/config/delay_profile/default.yaml @@ -0,0 +1,9 @@ +enable_usenet: true +enable_torrent: true +preferred_protocol: usenet +usenet_delay: 0 +torrent_delay: 0 +bypass_if_highest_quality: true +bypass_if_above_custom_format_score: false +minimum_custom_format_score: 0 +tags: [] diff --git a/config/download_client/NZBGet.yaml b/config/download_client/NZBGet.yaml new file mode 100644 index 0000000..9507d22 --- /dev/null +++ b/config/download_client/NZBGet.yaml @@ -0,0 +1,10 @@ +enable: true +priority: 1 +host: nzbget.service.consul +port: 443 +use_ssl: true +username: svc_nzbsubmit +password: "" +tv_category: tvseries +remove_completed_downloads: true +remove_failed_downloads: true diff --git a/config/indexer/NZBgeek.yaml b/config/indexer/NZBgeek.yaml new file mode 100644 index 0000000..ac12b0b --- /dev/null +++ b/config/indexer/NZBgeek.yaml @@ -0,0 +1,15 @@ +enable_automatic_search: true +enable_interactive_search: true +enable_rss: true +priority: 25 +base_url: "https://api.nzbgeek.info" +api_path: "/api" +api_key: "" +categories: + - 5040 + - 5045 + - 5060 + - 5070 + - 5080 +anime_categories: [] +season_search_maximum_single_episode_age: 0 diff --git a/config/indexer/NZBgeek_Prowlarr.yaml b/config/indexer/NZBgeek_Prowlarr.yaml new file mode 100644 index 0000000..971201a --- /dev/null +++ b/config/indexer/NZBgeek_Prowlarr.yaml @@ -0,0 +1,17 @@ +enable_automatic_search: true +enable_interactive_search: true +enable_rss: true +priority: 25 +base_url: "https://prowlarr.service.consul/1/" +api_path: "/api" +api_key: "" +categories: + - 5000 + - 5020 + - 5030 + - 5040 + - 5045 + - 5050 +anime_categories: + - 5070 +season_search_maximum_single_episode_age: 0 diff --git a/config/notification/Jellyfin.yaml b/config/notification/Jellyfin.yaml new file mode 100644 index 0000000..073a801 --- /dev/null +++ b/config/notification/Jellyfin.yaml @@ -0,0 +1,15 @@ +host: jellyfin.service.consul +port: 443 +use_ssl: true +api_key: "" +notify: false +update_library: true +on_grab: true +on_download: true +on_upgrade: true +on_rename: true +on_series_add: true +on_series_delete: true +on_episode_file_delete: true +on_episode_file_delete_for_upgrade: true +on_application_update: true diff --git a/config/quality_profile/BestQuality_1080p.yaml b/config/quality_profile/BestQuality_1080p.yaml new file mode 100644 index 0000000..57e1360 --- /dev/null +++ b/config/quality_profile/BestQuality_1080p.yaml @@ -0,0 +1,105 @@ +upgrade_allowed: true +cutoff: 7 +cutoff_format_score: 5000 +min_format_score: 0 +quality_groups: + - id: 4 + qualities: + - id: 4 + name: HDTV-720p + source: television + resolution: 720 + - id: 9 + qualities: + - id: 9 + name: HDTV-1080p + source: television + resolution: 1080 + - id: 14 + qualities: + - id: 14 + name: WEBRip-720p + source: webRip + resolution: 720 + - id: 5 + qualities: + - id: 5 + name: WEBDL-720p + source: web + resolution: 720 + - id: 6 + qualities: + - id: 6 + name: Bluray-720p + source: bluray + resolution: 720 + - id: 15 + qualities: + - id: 15 + name: WEBRip-1080p + source: webRip + resolution: 1080 + - id: 3 + qualities: + - id: 3 + name: WEBDL-1080p + source: web + resolution: 1080 + - id: 7 + qualities: + - id: 7 + name: Bluray-1080p + source: bluray + resolution: 1080 +format_items: + - name: release_AsmoFuscated + format: release_AsmoFuscated + score: 50 + - name: release_d3g + format: release_d3g + score: 50 + - name: release_iVy + format: release_iVy + score: 2000 + - name: format_av1 + format: format_av1 + score: -1000 + - name: size_0_800 + format: size_0_800 + score: 50 + - name: source_hdtv + format: source_hdtv + score: 50 + - name: source_webdl + format: source_webdl + score: 100 + - name: source_bluray + format: source_bluray + score: 200 + - name: size_3000_6000 + format: size_3000_6000 + score: 100 + - name: size_1500_3000 + format: size_1500_3000 + score: 200 + - name: size_800_1500 + format: size_800_1500 + score: 600 + - name: hvec_10bit + format: hvec_10bit + score: 100 + - name: resolution_2160p + format: resolution_2160p + score: -1000 + - name: resolution_720p + format: resolution_720p + score: 50 + - name: resolution_1080p + format: resolution_1080p + score: 500 + - name: format_x264 + format: format_x264 + score: -500 + - name: format_x265 + format: format_x265 + score: 1000 diff --git a/config/quality_profile/BestQuality_2160p.yaml b/config/quality_profile/BestQuality_2160p.yaml new file mode 100644 index 0000000..e49cf94 --- /dev/null +++ b/config/quality_profile/BestQuality_2160p.yaml @@ -0,0 +1,132 @@ +upgrade_allowed: true +cutoff: 19 +cutoff_format_score: 5000 +min_format_score: 0 +quality_groups: + - id: 4 + qualities: + - id: 4 + name: HDTV-720p + source: television + resolution: 720 + - id: 9 + qualities: + - id: 9 + name: HDTV-1080p + source: television + resolution: 1080 + - id: 14 + qualities: + - id: 14 + name: WEBRip-720p + source: webRip + resolution: 720 + - id: 5 + qualities: + - id: 5 + name: WEBDL-720p + source: web + resolution: 720 + - id: 6 + qualities: + - id: 6 + name: Bluray-720p + source: bluray + resolution: 720 + - id: 15 + qualities: + - id: 15 + name: WEBRip-1080p + source: webRip + resolution: 1080 + - id: 3 + qualities: + - id: 3 + name: WEBDL-1080p + source: web + resolution: 1080 + - id: 7 + qualities: + - id: 7 + name: Bluray-1080p + source: bluray + resolution: 1080 + - id: 16 + qualities: + - id: 16 + name: HDTV-2160p + source: television + resolution: 2160 + - id: 17 + qualities: + - id: 17 + name: WEBRip-2160p + source: webRip + resolution: 2160 + - id: 18 + qualities: + - id: 18 + name: WEBDL-2160p + source: web + resolution: 2160 + - id: 19 + qualities: + - id: 19 + name: Bluray-2160p + source: bluray + resolution: 2160 +format_items: + - name: hdr10 + format: hdr10 + score: 2000 + - name: release_AsmoFuscated + format: release_AsmoFuscated + score: 600 + - name: release_d3g + format: release_d3g + score: 1000 + - name: release_iVy + format: release_iVy + score: 400 + - name: format_av1 + format: format_av1 + score: -2000 + - name: source_hdtv + format: source_hdtv + score: 50 + - name: source_webdl + format: source_webdl + score: 100 + - name: source_bluray + format: source_bluray + score: 200 + - name: size_3000_6000 + format: size_3000_6000 + score: 300 + - name: size_6000_10000 + format: size_6000_10000 + score: 400 + - name: size_1500_3000 + format: size_1500_3000 + score: 200 + - name: size_800_1500 + format: size_800_1500 + score: 100 + - name: hvec_10bit + format: hvec_10bit + score: 500 + - name: resolution_2160p + format: resolution_2160p + score: 500 + - name: resolution_720p + format: resolution_720p + score: 50 + - name: resolution_1080p + format: resolution_1080p + score: 150 + - name: format_x265 + format: format_x265 + score: 1000 + - name: format_x264 + format: format_x264 + score: -1000 diff --git a/config/quality_profile/SD_OLD_SHOWS.yaml b/config/quality_profile/SD_OLD_SHOWS.yaml new file mode 100644 index 0000000..ffdfdbc --- /dev/null +++ b/config/quality_profile/SD_OLD_SHOWS.yaml @@ -0,0 +1,29 @@ +upgrade_allowed: false +cutoff: 1 +cutoff_format_score: 0 +min_format_score: 0 +quality_groups: + - id: 1 + qualities: + - id: 1 + name: SDTV + source: television + resolution: 480 + - id: 1000 + name: "WEB 480p" + qualities: + - id: 12 + name: WEBRip-480p + source: webRip + resolution: 480 + - id: 8 + name: WEBDL-480p + source: web + resolution: 480 + - id: 2 + qualities: + - id: 2 + name: DVD + source: dvd + resolution: 480 +format_items: [] diff --git a/config/root_folder/tvseries.yaml b/config/root_folder/tvseries.yaml new file mode 100644 index 0000000..0a10bee --- /dev/null +++ b/config/root_folder/tvseries.yaml @@ -0,0 +1 @@ +path: "/shared/media/tvseries" diff --git a/environments/root.hcl b/environments/root.hcl new file mode 100644 index 0000000..ffbe4d5 --- /dev/null +++ b/environments/root.hcl @@ -0,0 +1,32 @@ +generate "backend" { + path = "backend.tf" + if_exists = "overwrite" + contents = <