--- # --- Media library directories --- - name: Create media library directories ansible.builtin.file: path: "{{ item }}" state: directory owner: "{{ jellyfin_puid }}" group: "{{ jellyfin_pgid }}" mode: "0755" loop: - /media/movies - /media/tv - /media/transcodes # --- qBittorrent: pre-seed credentials on fresh install --- - name: Check if qBittorrent config exists ansible.builtin.stat: path: /containers/qbittorrent/config/qBittorrent/qBittorrent.conf register: _qbt_conf - name: Deploy qBittorrent credential seed script ansible.builtin.template: src: qbittorrent_seed.py.j2 dest: /tmp/qbt_seed.py mode: "0700" when: not _qbt_conf.stat.exists - name: Pre-seed qBittorrent credentials ansible.builtin.command: cmd: python3 /tmp/qbt_seed.py when: not _qbt_conf.stat.exists # --- Wait for services to be ready --- - name: Read Radarr config ansible.builtin.slurp: src: /containers/radarr/config/config.xml register: _radarr_config - name: Read Sonarr config ansible.builtin.slurp: src: /containers/sonarr/config/config.xml register: _sonarr_config - name: Read Prowlarr config ansible.builtin.slurp: src: /containers/prowlarr/config/config.xml register: _prowlarr_config - name: Set API key facts ansible.builtin.set_fact: _radarr_key: "{{ (_radarr_config.content | b64decode | regex_search('(.*?)', '\\1'))[0] }}" _sonarr_key: "{{ (_sonarr_config.content | b64decode | regex_search('(.*?)', '\\1'))[0] }}" _prowlarr_key: "{{ (_prowlarr_config.content | b64decode | regex_search('(.*?)', '\\1'))[0] }}" - name: Wait for Radarr ansible.builtin.uri: url: "http://localhost:7878/api/v3/system/status" headers: X-Api-Key: "{{ _radarr_key }}" register: _r until: _r.status == 200 retries: 12 delay: 5 - name: Wait for Sonarr ansible.builtin.uri: url: "http://localhost:8989/api/v3/system/status" headers: X-Api-Key: "{{ _sonarr_key }}" register: _r until: _r.status == 200 retries: 12 delay: 5 - name: Wait for Prowlarr ansible.builtin.uri: url: "http://localhost:9696/api/v1/system/status" headers: X-Api-Key: "{{ _prowlarr_key }}" register: _r until: _r.status == 200 retries: 12 delay: 5 # --- Prowlarr: fix TPB season search --- # apibay.org fulltext indexes "s05e01" as a single token, not "s05". When Prowlarr # appends the season number to the query (the.boys.s05), apibay returns 0 results. # Removing season/ep from the tv-search capability makes Prowlarr search with just # the show title; Sonarr's parser then filters the results by season/episode. # The file is locked read-only so Prowlarr cannot revert it on definition refresh. - name: Wait for Prowlarr to write TPB definition file ansible.builtin.stat: path: /containers/prowlarr/config/Definitions/thepiratebay.yml register: _tpb_def until: _tpb_def.stat.exists retries: 12 delay: 5 - name: Fix TPB tv-search to not append season to query ansible.builtin.lineinfile: path: /containers/prowlarr/config/Definitions/thepiratebay.yml regexp: '^\s+tv-search: \[q, season, ep\]' line: ' tv-search: [q]' register: _tpb_fix - name: Lock TPB definition read-only to prevent Prowlarr reverting the fix ansible.builtin.file: path: /containers/prowlarr/config/Definitions/thepiratebay.yml mode: "0444" - name: Restart Prowlarr to apply TPB definition fix ansible.builtin.command: cmd: docker restart prowlarr changed_when: true when: _tpb_fix.changed - name: Wait for Prowlarr after restart ansible.builtin.uri: url: "http://localhost:9696/api/v1/system/status" headers: X-Api-Key: "{{ _prowlarr_key }}" register: _r until: _r.status == 200 retries: 12 delay: 5 when: _tpb_fix.changed # --- Radarr: download client --- - name: Get Radarr download clients ansible.builtin.uri: url: "http://localhost:7878/api/v3/downloadclient" headers: X-Api-Key: "{{ _radarr_key }}" register: _radarr_dc - name: Add qBittorrent to Radarr ansible.builtin.uri: url: "http://localhost:7878/api/v3/downloadclient" method: POST headers: X-Api-Key: "{{ _radarr_key }}" body_format: json body: name: qBittorrent enable: true protocol: torrent priority: 1 implementation: QBittorrent configContract: QBittorrentSettings fields: - name: host value: qbittorrent - name: port value: 5080 - name: useSsl value: false - name: username value: admin - name: password value: "{{ qbittorrent_webui_password }}" - name: movieCategory value: radarr tags: [] status_code: 201 when: _radarr_dc.json | selectattr('name', 'equalto', 'qBittorrent') | list | length == 0 # --- Radarr: root folder --- - name: Get Radarr root folders ansible.builtin.uri: url: "http://localhost:7878/api/v3/rootfolder" headers: X-Api-Key: "{{ _radarr_key }}" register: _radarr_rf - name: Add movies root folder to Radarr ansible.builtin.uri: url: "http://localhost:7878/api/v3/rootfolder" method: POST headers: X-Api-Key: "{{ _radarr_key }}" body_format: json body: path: /downloads/movies status_code: 201 when: _radarr_rf.json | selectattr('path', 'equalto', '/downloads/movies') | list | length == 0 # --- Sonarr: download client --- - name: Get Sonarr download clients ansible.builtin.uri: url: "http://localhost:8989/api/v3/downloadclient" headers: X-Api-Key: "{{ _sonarr_key }}" register: _sonarr_dc - name: Add qBittorrent to Sonarr ansible.builtin.uri: url: "http://localhost:8989/api/v3/downloadclient" method: POST headers: X-Api-Key: "{{ _sonarr_key }}" body_format: json body: name: qBittorrent enable: true protocol: torrent priority: 1 implementation: QBittorrent configContract: QBittorrentSettings fields: - name: host value: qbittorrent - name: port value: 5080 - name: useSsl value: false - name: username value: admin - name: password value: "{{ qbittorrent_webui_password }}" - name: tvCategory value: sonarr tags: [] status_code: 201 when: _sonarr_dc.json | selectattr('name', 'equalto', 'qBittorrent') | list | length == 0 # --- Sonarr: root folder --- - name: Get Sonarr root folders ansible.builtin.uri: url: "http://localhost:8989/api/v3/rootfolder" headers: X-Api-Key: "{{ _sonarr_key }}" register: _sonarr_rf - name: Add TV root folder to Sonarr ansible.builtin.uri: url: "http://localhost:8989/api/v3/rootfolder" method: POST headers: X-Api-Key: "{{ _sonarr_key }}" body_format: json body: path: /downloads/tv status_code: 201 when: _sonarr_rf.json | selectattr('path', 'equalto', '/downloads/tv') | list | length == 0 # --- Prowlarr: apps --- - name: Get Prowlarr applications ansible.builtin.uri: url: "http://localhost:9696/api/v1/applications" headers: X-Api-Key: "{{ _prowlarr_key }}" register: _prowlarr_apps - name: Add Radarr to Prowlarr ansible.builtin.uri: url: "http://localhost:9696/api/v1/applications" method: POST headers: X-Api-Key: "{{ _prowlarr_key }}" body_format: json body: name: Radarr syncLevel: fullSync implementation: Radarr configContract: RadarrSettings fields: - name: prowlarrUrl value: "http://prowlarr:9696" - name: baseUrl value: "http://radarr:7878" - name: apiKey value: "{{ _radarr_key }}" - name: syncCategories value: [2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060] tags: [] status_code: 201 when: _prowlarr_apps.json | selectattr('name', 'equalto', 'Radarr') | list | length == 0 - name: Add Sonarr to Prowlarr ansible.builtin.uri: url: "http://localhost:9696/api/v1/applications" method: POST headers: X-Api-Key: "{{ _prowlarr_key }}" body_format: json body: name: Sonarr syncLevel: fullSync implementation: Sonarr configContract: SonarrSettings fields: - name: prowlarrUrl value: "http://prowlarr:9696" - name: baseUrl value: "http://sonarr:8989" - name: apiKey value: "{{ _sonarr_key }}" - name: syncCategories value: [5000, 5010, 5020, 5030, 5040, 5045, 5050, 5060, 5070, 5080] tags: [] status_code: 201 when: _prowlarr_apps.json | selectattr('name', 'equalto', 'Sonarr') | list | length == 0 # --- Prowlarr: FlareSolverr proxy --- - name: Get Prowlarr indexer proxies ansible.builtin.uri: url: "http://localhost:9696/api/v1/indexerproxy" headers: X-Api-Key: "{{ _prowlarr_key }}" register: _prowlarr_proxies - name: Add FlareSolverr proxy to Prowlarr ansible.builtin.uri: url: "http://localhost:9696/api/v1/indexerproxy" method: POST headers: X-Api-Key: "{{ _prowlarr_key }}" body_format: json body: name: FlareSolverr implementation: FlareSolverr configContract: FlareSolverrSettings tags: [1] fields: - name: host value: "http://flaresolverr:8191" - name: requestTimeout value: 60 status_code: 201 when: _prowlarr_proxies.json | selectattr('name', 'equalto', 'FlareSolverr') | list | length == 0 # --- Prowlarr: indexers --- - name: Get Prowlarr indexers ansible.builtin.uri: url: "http://localhost:9696/api/v1/indexer" headers: X-Api-Key: "{{ _prowlarr_key }}" register: _prowlarr_indexers - name: Add YTS indexer ansible.builtin.uri: url: "http://localhost:9696/api/v1/indexer" method: POST headers: X-Api-Key: "{{ _prowlarr_key }}" body_format: json body: name: YTS enable: true appProfileId: 1 priority: 25 implementation: Cardigann configContract: CardigannSettings tags: [] fields: - name: definitionFile value: yts - name: baseSettings.limitsUnit value: 0 - name: torrentBaseSettings.preferMagnetUrl value: false - name: apiurl value: movies-api.accel.li status_code: 201 when: _prowlarr_indexers.json | selectattr('name', 'equalto', 'YTS') | list | length == 0 - name: Add The Pirate Bay indexer ansible.builtin.uri: url: "http://localhost:9696/api/v1/indexer" method: POST headers: X-Api-Key: "{{ _prowlarr_key }}" body_format: json body: name: The Pirate Bay enable: true appProfileId: 1 priority: 25 implementation: Cardigann configContract: CardigannSettings tags: [] fields: - name: definitionFile value: thepiratebay - name: baseSettings.limitsUnit value: 0 - name: torrentBaseSettings.preferMagnetUrl value: false - name: apiurl value: apibay.org status_code: 201 when: _prowlarr_indexers.json | selectattr('name', 'equalto', 'The Pirate Bay') | list | length == 0 - name: Add EZTV indexer ansible.builtin.uri: url: "http://localhost:9696/api/v1/indexer" method: POST timeout: 90 headers: X-Api-Key: "{{ _prowlarr_key }}" body_format: json body: name: EZTV enable: true appProfileId: 1 priority: 25 implementation: Cardigann configContract: CardigannSettings tags: [1] fields: - name: definitionFile value: eztv - name: baseSettings.limitsUnit value: 0 - name: torrentBaseSettings.preferMagnetUrl value: false status_code: 201 when: _prowlarr_indexers.json | selectattr('name', 'equalto', 'EZTV') | list | length == 0 ignore_errors: true - name: Add 1337x indexer ansible.builtin.uri: url: "http://localhost:9696/api/v1/indexer" method: POST timeout: 90 headers: X-Api-Key: "{{ _prowlarr_key }}" body_format: json body: name: 1337x enable: true appProfileId: 1 priority: 25 implementation: Cardigann configContract: CardigannSettings tags: [1] fields: - name: definitionFile value: 1337x - name: baseSettings.limitsUnit value: 0 - name: torrentBaseSettings.preferMagnetUrl value: false - name: downloadlink value: 0 - name: downloadlink2 value: 1 - name: sort value: 2 - name: type value: 1 status_code: 201 when: _prowlarr_indexers.json | selectattr('name', 'equalto', '1337x') | list | length == 0 ignore_errors: true - name: Sync Prowlarr indexers to apps ansible.builtin.uri: url: "http://localhost:9696/api/v1/applications/syncindexers" method: GET headers: X-Api-Key: "{{ _prowlarr_key }}" status_code: [200, 202] ignore_errors: true # --- Jellyseerr: connect Sonarr and Radarr --- - name: Check if Jellyseerr is initialized ansible.builtin.stat: path: /containers/jellyseerr/config/settings.json register: _js_settings - name: Read Jellyseerr settings ansible.builtin.slurp: src: /containers/jellyseerr/config/settings.json register: _js_raw when: _js_settings.stat.exists - name: Set Jellyseerr API key fact ansible.builtin.set_fact: _js_key: "{{ (_js_raw.content | b64decode | from_json).main.apiKey }}" when: _js_settings.stat.exists - name: Get Jellyseerr Sonarr servers ansible.builtin.uri: url: "http://localhost:5055/api/v1/settings/sonarr" headers: X-Api-Key: "{{ _js_key }}" register: _js_sonarr when: _js_settings.stat.exists - name: Configure Sonarr in Jellyseerr ansible.builtin.uri: url: "http://localhost:5055/api/v1/settings/sonarr/{{ '0' if (_js_sonarr.json | length == 0) else (_js_sonarr.json[0].id | string) }}" method: "{{ 'POST' if (_js_sonarr.json | length == 0) else 'PUT' }}" headers: X-Api-Key: "{{ _js_key }}" body_format: json body: name: sonarr hostname: sonarr port: 8989 apiKey: "{{ _sonarr_key }}" useSsl: false baseUrl: "" activeProfileId: 4 activeProfileName: HD-1080p activeDirectory: /downloads/tv tags: [] animeTags: [] is4k: false isDefault: true enableSeasonFolders: true syncEnabled: false preventSearch: false tagRequests: false status_code: [200, 201] when: > _js_settings.stat.exists and (_js_sonarr.json | length == 0 or not _js_sonarr.json[0].isDefault) - name: Get Jellyseerr Radarr servers ansible.builtin.uri: url: "http://localhost:5055/api/v1/settings/radarr" headers: X-Api-Key: "{{ _js_key }}" register: _js_radarr when: _js_settings.stat.exists - name: Configure Radarr in Jellyseerr ansible.builtin.uri: url: "http://localhost:5055/api/v1/settings/radarr/{{ '0' if (_js_radarr.json | length == 0) else (_js_radarr.json[0].id | string) }}" method: "{{ 'POST' if (_js_radarr.json | length == 0) else 'PUT' }}" headers: X-Api-Key: "{{ _js_key }}" body_format: json body: name: radarr hostname: radarr port: 7878 apiKey: "{{ _radarr_key }}" useSsl: false baseUrl: "" activeProfileId: 4 activeProfileName: HD-1080p activeDirectory: /downloads/movies is4k: false minimumAvailability: released tags: [] isDefault: true syncEnabled: false preventSearch: false tagRequests: false status_code: [200, 201] when: > _js_settings.stat.exists and (_js_radarr.json | length == 0 or not _js_radarr.json[0].isDefault)