{"id":21423136,"url":"https://github.com/tommyvange/arrstalledhandler","last_synced_at":"2025-03-16T20:25:22.107Z","repository":{"id":263903020,"uuid":"891742630","full_name":"tommyvange/ArrStalledHandler","owner":"tommyvange","description":"ArrStalledHandler is a Python script to manage stalled downloads in Radarr and Sonarr. It identifies stalled downloads, tracks them in a database, and performs configurable actions such as removing, blocklisting, or re-searching. Easily deployable via Docker with customizable settings for seamless automation.","archived":false,"fork":false,"pushed_at":"2025-03-05T12:28:00.000Z","size":127,"stargazers_count":24,"open_issues_count":5,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-05T13:29:42.786Z","etag":null,"topics":["docker","docker-compose","dockerfile","downloading-metadata","python","python3","qbittorrent","radarr","sonarr","stalled"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tommyvange.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-20T21:54:22.000Z","updated_at":"2025-03-05T12:26:55.000Z","dependencies_parsed_at":"2025-03-05T13:26:26.376Z","dependency_job_id":"10c80762-b1a7-43ae-84d9-66ca106f0cbf","html_url":"https://github.com/tommyvange/ArrStalledHandler","commit_stats":null,"previous_names":["tommyvange/arrstalledhandler"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyvange%2FArrStalledHandler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyvange%2FArrStalledHandler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyvange%2FArrStalledHandler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyvange%2FArrStalledHandler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tommyvange","download_url":"https://codeload.github.com/tommyvange/ArrStalledHandler/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243926604,"owners_count":20370010,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","docker-compose","dockerfile","downloading-metadata","python","python3","qbittorrent","radarr","sonarr","stalled"],"created_at":"2024-11-22T21:14:28.791Z","updated_at":"2025-03-16T20:25:22.099Z","avatar_url":"https://github.com/tommyvange.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ArrStalledHandler\n\nArrStalledHandler is a Python-based script designed to handle stalled downloads in [Radarr](https://radarr.video/), [Sonarr](https://sonarr.tv/), [Lidarr](https://lidarr.audio/) and [Readarr](https://readarr.com/) by taking actions such as removing, blocklisting, or blocklisting and re-searching for the affected items. It supports configuration through a `.env` file, logging for visibility, and is deployable via Docker for ease of use.\n\nThis repository is licensed under the **[GNU General Public License v3.0 (GPLv3)](LICENSE)**.\n\nCreated by **[Tommy Vange Rød](https://github.com/tommyvange)**. You can see the full list of credits [here](#credits).\n\nThis project is available on [GitHub](https://github.com/tommyvange/ArrStalledHandler), [Docker Hub](https://hub.docker.com/r/tommythebeast/arrstalledhandler) and the [Unraid Community App store](#unraid-deployment). \n\n[![Publish Docker image](https://github.com/tommyvange/ArrStalledHandler/actions/workflows/publish-docker-image.yml/badge.svg)](https://github.com/tommyvange/ArrStalledHandler/actions/workflows/publish-docker-image.yml)\n[![Check docker build](https://github.com/tommyvange/ArrStalledHandler/actions/workflows/check-docker-image.yml/badge.svg?branch=main)](https://github.com/tommyvange/ArrStalledHandler/actions/workflows/check-docker-image.yml)\n\n----------\n\n## Features\n\n-   **Automatic Handling of Stalled Downloads**:\n    -   Detect stalled downloads based on error messages from Radarr/Sonarr/Lidarr/Readarr queues.\n    - Detect download stuck on \"Downloading Metadata\" in qBittorrent and treat them as stalled.\n    -   Perform configurable actions such as:\n        -   Remove the stalled download.\n        -   Blocklist the stalled download.\n        -   Blocklist and re-trigger a search for the movie or episodes.\n-   **Database Tracking**:\n    -   Tracks stalled downloads in a SQLite database to ensure actions are only taken after a specified timeout period.\n-   **Logging**:\n    -   Verbose and informative logging controlled via configuration.\n-   **Docker Support**:\n    -   Easily deployable with Docker and customizable run intervals.\n\n----------\n\n\n## Configuration\n\nThe script is fully configurable using environment variables specified in a `.env` file. Below is a description of each configuration option:\n\n### `.env` Variables\n\n| Variable                                | Description                                                                                     | Default Value          |\n|-----------------------------------------|-------------------------------------------------------------------------------------------------|------------------------|\n| `RADARR_URL`                            | The base URL for Radarr's API. Example: `http://localhost:7878,http://otherhost:7878`.          | None (required)        |\n| `RADARR_API_KEY`                        | The API key for Radarr (found in Radarr settings).                                              | None (required)        |\n| `SONARR_URL`                            | The base URL for Sonarr's API. Example: `http://localhost:8989,http://otherhost:8989`.          | None (required)        |\n| `SONARR_API_KEY`                        | The API key for Sonarr (found in Sonarr settings).                                              | None (required)        |\n| `LIDARR_URL`                            | The base URL for Lidarr's API. Example: `http://localhost:8686,http://otherhost:8686`.          | None (required)        |\n| `LIDARR_API_KEY`                        | The API key for Lidarr (found in Radarr settings).                                              | None (required)        |\n| `READARR_URL`                           | The base URL for Readarr's API. Example: `http://localhost:8787,http://otherhost:8787`.         | None (required)        |\n| `READARR_API_KEY`                       | The API key for Readarr (found in Sonarr settings).                                             | None (required)        |\n| `STALLED_TIMEOUT`                       | Time (in seconds) a download must remain stalled before actions are taken.                      | `3600` (1 hour)        |\n| `STALLED_ACTION`                        | Action to perform on stalled downloads: `REMOVE`, `BLOCKLIST`, or `BLOCKLIST_AND_SEARCH`.       | `BLOCKLIST_AND_SEARCH` |\n| `VERBOSE`                               | Enable verbose logging for debugging (`true` or `false`).                                       | `false`                |\n| `RUN_INTERVAL`                          | Time (in seconds) between script executions when running in Docker.                             | `300` (5 minutes)      |\n| `COUNT_DOWNLOADING_METADATA_AS_STALLED` | Weather the script should count downloads with the status of \"Downloading Metadata\" as stalled. | `false`                |\n\nTo disable Radarr or Sonarr; leave the URL empty in the environment. If the service does not have a URL, then it is skipped. Multple services are allowed by using comma seperated values.\n\n----------\n\n## How It Works\n\n### Script Workflow\n\n1.  **Initialization**:\n    \n    -   The script initializes a SQLite database (`stalled_downloads.db`) to track stalled downloads.\n    -   It fetches the current queue from Radarr and Sonarr APIs.\n2.  **Detect Stalled Downloads**:\n    \n    -   The script identifies stalled downloads based on the error message: `\"The download is stalled with no connections\"`.\n    -   [Optional] The script treats downloads with the error message `\"qBittorrent is downloading metadata\"` as stalled.\n3.  **Timeout Check**:\n    \n    -   Downloads are only handled if they have been stalled longer than the configured `STALLED_TIMEOUT`.\n4.  **Perform Configured Action**:\n    \n    -   Based on the `STALLED_ACTION` setting:\n        -   **REMOVE**: Removes the stalled download.\n        -   **BLOCKLIST**: Removes and blocklists the stalled download.\n        -   **BLOCKLIST_AND_SEARCH**: Removes, blocklists, and re-triggers a search for the movie or episodes.\n5.  **Logging**:\n    \n    -   Logs detailed information about each action for visibility.\n6.  **Repeat**:\n    \n    -   When running in Docker, the script waits for the `RUN_INTERVAL` duration and repeats the process.\n\n----------\n\n## Deployment\n### Unraid Deployment\n![Picture of application in the Unraid Community App store](https://i.ibb.co/BNghTZN/image-png-fe1039ecc35d3aa9ffc37541edbd5e0d.jpg)\n1. Install the Community Apps extension as documented in [this guide](https://forums.unraid.net/topic/38582-plug-in-community-applications/).\n2. Go to the **Apps**-section in your Unraid web-ui.\n3. Search for **ArrStalledHandler**.\n4. Click **Install** on the application.\n5. Fill out the variables according to the [Configuration](#configuration).\n6. Click **Apply**.\n\nNow the container should automatically start up and start handling your stalled downloads.\n\n### Docker Deployment ([Docker Hub](https://hub.docker.com/r/tommythebeast/arrstalledhandler))\n\n**Docker compose**\n\nMore info at [Docker Docs](https://docs.docker.com/compose/intro/compose-application-model/).\n``` yaml\nservices:\n  arr-stalled-handler:\n    image: tommythebeast/arrstalledhandler:latest\n    container_name: ArrStalledHandler\n    restart: unless-stopped\n    environment:\n      RADARR_URL: \"http://localhost:7878,http://otherhost:7878\"\n      RADARR_API_KEY: \"your_radarr_api_key,your_2nd_radarr_api_key\"\n      SONARR_URL: \"http://localhost:8989,http://otherhost:8989\"\n      SONARR_API_KEY: \"your_sonarr_api_key,your_2nd_sonarr_api_key\"\n      LIDARR_URL: \"http://localhost:8686,http://otherhost:8686\"\n      LIDARR_API_KEY: \"your_lidarr_api_key,your_2nd_lidarr_api_key\"\n      READARR_URL: \"http://localhost:8787,http://otherhost:8787\"\n      READARR_API_KEY: \"your_readarr_api_key,our_2nd_readarr_api_key\"\n      STALLED_TIMEOUT: \"3600\"\n      STALLED_ACTION: \"BLOCKLIST_AND_SEARCH\"\n      VERBOSE: \"false\"\n      RUN_INTERVAL: \"300\"\n      COUNT_DOWNLOADING_METADATA_AS_STALLED: \"false\"\n```\n\n**Docker CLI**\n\nMore info at [Docker Docs](https://docs.docker.com/engine/containers/run/).\n\n*Multi-line:*\n``` bash\ndocker run -d \\\n  --name=ArrStalledHandler \\\n  -e RADARR_URL=http://localhost:7878,http://otherhost:7878 \\\n  -e RADARR_API_KEY=your_radarr_api_key,your_2nd_radarr_api_key \\\n  -e SONARR_URL=http://localhost:8989,http://otherhost:8989 \\\n  -e SONARR_API_KEY=your_sonarr_api_key,your_2nd_sonarr_api_key \\\n  -e LIDARR_URL=http://localhost:8686,http://otherhost:8686 \\\n  -e LIDARR_API_KEY=your_lidarr_api_key,your_2nd_lidarr_api_key \\\n  -e READARR_URL=http://localhost:8787,http://otherhost:8787 \\\n  -e READARR_API_KEY=your_readarr_api_key,our_2nd_readarr_api_key \\\n  -e STALLED_TIMEOUT=3600 \\\n  -e STALLED_ACTION=BLOCKLIST_AND_SEARCH \\\n  -e VERBOSE=false \\\n  -e RUN_INTERVAL=300 \\\n  -e COUNT_DOWNLOADING_METADATA_AS_STALLED=false \\\n  --restart unless-stopped \\\n  tommythebeast/arrstalledhandler:latest\n```\n\n*One line:*\n``` bash\ndocker run -d --name=ArrStalledHandler -e RADARR_URL=http://localhost:7878,http://otherhost:7878 -e RADARR_API_KEY=your_radarr_api_key,your_2nd_radarr_api_key -e SONARR_URL=http://localhost:8989,http://otherhost:8989 -e SONARR_API_KEY=your_sonarr_api_key,your_2nd_sonarr_api_key -e LIDARR_URL=http://localhost:8686,http://otherhost:8686  -e LIDARR_API_KEY=your_lidarr_api_key,your_2nd_lidarr_api_key -e READARR_URL=http://localhost:8787,http://otherhost:8787 -e READARR_API_KEY=your_readarr_api_key,our_2nd_readarr_api_key -e STALLED_TIMEOUT=3600 -e STALLED_ACTION=BLOCKLIST_AND_SEARCH -e VERBOSE=false -e RUN_INTERVAL=300 -e COUNT_DOWNLOADING_METADATA_AS_STALLED=false --restart unless-stopped tommythebeast/arrstalledhandler:latest\n```\n\n### Docker Deployment (Manual)\n\n1.  **Clone the Repository**:\n    \n    ``` bash\n    git clone https://github.com/your-username/ArrStalledHandler.git\n    cd ArrStalledHandler\n    ```\n    \n2.  **Configure Environment**:\n    \n    Create a `.env` file and populate it with the required variables:\n \n    ``` env\n    RADARR_URL=http://localhost:7878,http://otherhost:7878\n    RADARR_API_KEY=aaaabbbbcccc111122223333,xxxxyyyyzzzz777788889999\n    SONARR_URL=http://localhost:8989,http://otherhost:8989\n    SONARR_API_KEY=aaaabbbbcccc111122223333,xxxxyyyyzzzz777788889999\n    LIDARR_URL=http://localhost:8686,http://otherhost:8686\n    LIDARR_API_KEY=aaaabbbbcccc111122223333,xxxxyyyyzzzz777788889999\n    READARR_URL=http://localhost:8787,http://otherhost:8787\n    READARR_API_KEY=aaaabbbbcccc111122223333,xxxxyyyyzzzz777788889999\n    STALLED_TIMEOUT=3600\n    STALLED_ACTION=BLOCKLIST_AND_SEARCH\n    VERBOSE=false\n    RUN_INTERVAL=300\n    COUNT_DOWNLOADING_METADATA_AS_STALLED=false\n    ```\n\n3.  **Build the Docker Image**:\n    \n    ``` bash\n    docker-compose build .\n    ```\n    \n4.  **Run the Docker Container**:\n    \n    ``` bash\n    docker-compose up -d\n    ```\n\n### Local Installation\n\n*Requires Python 3.13*\n\n1.  **Clone the Repository**:\n    \n    ``` bash\n    git clone https://github.com/your-username/ArrStalledHandler.git\n    cd ArrStalledHandler\n    ```\n    \n2.  **Install Dependencies**:\n    ``` bash\n    pip install -r requirements.txt\n    ```\n        \n3.  **Configure Environment**:\n    \n    Create a `.env` file and populate it with the required variables:\n \n    ``` env\n    RADARR_URL=http://localhost:7878,http://otherhost:7878\n    RADARR_API_KEY=aaaabbbbcccc111122223333,xxxxyyyyzzzz777788889999\n    SONARR_URL=http://localhost:8989,http://otherhost:8989\n    SONARR_API_KEY=aaaabbbbcccc111122223333,xxxxyyyyzzzz777788889999\n    LIDARR_URL=http://localhost:8686,http://otherhost:8686\n    LIDARR_API_KEY=aaaabbbbcccc111122223333,xxxxyyyyzzzz777788889999\n    READARR_URL=http://localhost:8787,http://otherhost:8787\n    READARR_API_KEY=aaaabbbbcccc111122223333,xxxxyyyyzzzz777788889999\n    STALLED_TIMEOUT=3600\n    STALLED_ACTION=BLOCKLIST_AND_SEARCH\n    VERBOSE=false\n    RUN_INTERVAL=300\n    COUNT_DOWNLOADING_METADATA_AS_STALLED=false\n    ```\n        \n4.  **Run the Script**:\n    \n    ``` bash\n    python main.py\n    ```\n\n----------\n\n## Logging\n\n-   Logs are written to the console and are controlled by the `VERBOSE` environment variable.\n-   If `VERBOSE` is set to `true`, debug-level logs are enabled. \n\nExample log output:\n    \n``` text\nINFO: Checking stalled downloads in Radarr...\nINFO: Handling Download ID 1462067687 in Radarr (elapsed time: 400 seconds).\nINFO: Triggering search for Movie ID 770 in Radarr using Command API...\nINFO: Script execution completed. Sleeping for 300 seconds...\n```\n    \n\n----------\n\n## Troubleshooting\n\n1. **Script Not Executing Actions**:\n    -   Check if `STALLED_TIMEOUT` is too high.\n    -   Verify the stalled downloads are correctly detected via Radarr/Sonarr queues.\n\n----------\n\n## Credits\n\n### Author\n\n\u003c!-- readme: tommyvange -start --\u003e\n\u003ctable\u003e\n\t\u003ctbody\u003e\n\t\t\u003ctr\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/tommyvange\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/28400191?v=4\" width=\"100;\" alt=\"tommyvange\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eTommy Vange Rød\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\u003ctbody\u003e\n\u003c/table\u003e\n\u003c!-- readme: tommyvange -end --\u003e\n\nYou can find more of my work on my [GitHub profile](https://github.com/tommyvange) or connect with me on [LinkedIn](https://www.linkedin.com/in/tommyvange/).\n\n### Contributors\nHuge thanks to everyone who dedicates their valuable time to improving, perfecting, and supporting this project!\n\n\u003c!-- readme: contributors,tommyvange/- -start --\u003e\n\u003ctable\u003e\n\t\u003ctbody\u003e\n\t\t\u003ctr\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/supernovatroubling\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/58279766?v=4\" width=\"100;\" alt=\"supernovatroubling\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003esupernovatroubling\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\u003ctbody\u003e\n\u003c/table\u003e\n\u003c!-- readme: contributors,tommyvange/- -end --\u003e\n\n----------\n\n# GNU General Public License v3.0 (GPLv3)\n\nThe  **GNU General Public License v3.0 (GPLv3)**  is a free, copyleft license for software and other creative works. It ensures your freedom to share, modify, and distribute all versions of a program, keeping it free software for everyone.\n\nFull license can be read [here](LICENSE) or at [gnu.org](https://www.gnu.org/licenses/gpl-3.0.en.html#license-text).\n\n## Key Points:\n\n1.  **Freedom to Share and Change:**\n    \n    -   You can distribute copies of GPLv3-licensed software.\n    -   Access the source code.\n    -   Modify the software.\n    -   Create new free programs using parts of it.\n2.  **Responsibilities:**\n    \n    -   If you distribute GPLv3 software, pass on the same freedoms to recipients.\n    -   Provide the source code.\n    -   Make recipients aware of their rights.\n3.  **No Warranty:**\n    \n    -   No warranty for this free software.\n    -   Developers protect your rights through copyright and this license.\n4.  **Marking Modifications:**\n    \n    -   Clearly mark modified versions to avoid attributing problems to previous authors.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftommyvange%2Farrstalledhandler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftommyvange%2Farrstalledhandler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftommyvange%2Farrstalledhandler/lists"}