Skip to content

Conversation

@BaukeZwart
Copy link
Contributor

@BaukeZwart BaukeZwart commented Nov 27, 2025

Pull Request

Purpose

Add support for unlimited numbers of qBittorrent instances in the mover-tuner scripts

Approach

mover-tuning-start.sh
Version: 1.0.0 → 1.1.0
Added support for more than 2 qBittorrent instances
Added config format detection functions for backward Compatibility
Added array-based configuration support
Fixed version comparison logic
Enhanced validation for array configuration

mover-tuning-end.sh
Version: 1.0.0 → 1.1.0
Added support for more than 2 qBittorrent instances
Added config format detection functions for backward Compatibility
Added array-based configuration support
Fixed version comparison logic
Enhanced validation for array configuration

Version: 1.0.0 → 1.1.0
Changed BitTorrent config from legacy to array format
Backward Compatibility

Open Questions and Pre-Merge TODOs

Requirements

Summary by Sourcery

Add array-based configuration and format detection to the mover-tuning scripts to support an arbitrary number of qBittorrent instances while maintaining backward compatibility with the legacy two-instance setup.

New Features:

  • Support configuring an unlimited number of qBittorrent instances via array-based settings in mover-tuning.cfg.

Enhancements:

  • Introduce automatic detection of legacy vs array-based configuration formats in the mover-tuning scripts for smooth migration.
  • Improve configuration validation to ensure consistent qBittorrent instance definitions and log the active configuration mode.
  • Update version comparison logic in the mover-tuning scripts to correctly recognize when local script or config versions are newer than the remote versions.

Documentation:

  • Clarify configuration comments in mover-tuning.cfg to document the new array-based instance format and constraints.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Nov 27, 2025

Reviewer's Guide

Adds array-based configuration and format detection to mover-tuning start/end scripts to support an arbitrary number of qBittorrent instances while keeping backward compatibility with the legacy 1–2 instance config, and fixes version comparison plus config validation accordingly.

Class diagram for legacy vs array qBittorrent configuration model

classDiagram
    class LegacyConfig {
        <<config>>
        bool ENABLE_QBIT_2
        string QBIT_NAME_1
        string QBIT_HOST_1
        string QBIT_USER_1
        string QBIT_PASS_1
        string QBIT_NAME_2
        string QBIT_HOST_2
        string QBIT_USER_2
        string QBIT_PASS_2
    }

    class ArrayConfig {
        <<config>>
        string[] NAMES
        string[] HOSTS
        string[] USERS
        string[] PASSWORDS
    }

    class ScriptSettings {
        <<config>>
        int DAYS_FROM
        int DAYS_TO
        string CACHE_MOUNT
        string QBIT_MOVER_PATH
    }

    class ConfigHandler {
        <<utility>>
        string detect_config_format()
        int get_instance_count()
        string get_instance_details(index)
        void validate_config()
        void check_script_version()
        void check_config_version()
    }

    ConfigHandler --> LegacyConfig : supports_legacy
    ConfigHandler --> ArrayConfig : supports_array
    ConfigHandler --> ScriptSettings : validates

    class InstanceProcessing {
        <<logic>>
        void main()
        void process_qbit_instance(name, host, user, password)
    }

    InstanceProcessing --> ConfigHandler : uses
    InstanceProcessing --> LegacyConfig : reads_if_legacy
    InstanceProcessing --> ArrayConfig : reads_if_array
    InstanceProcessing --> ScriptSettings : uses_for_limits
Loading

File-Level Changes

Change Details Files
Add array-based, unlimited-instance qBittorrent configuration with backward-compatible format detection and iteration in mover-tuning start/end scripts.
  • Introduce detect_config_format, get_instance_count, and get_instance_details helpers to abstract legacy vs array-based configuration access.
  • Replace hard-coded handling of one or two qBittorrent instances with a loop over the detected number of instances, using parsed instance details for processing.
  • Extend validate_config to validate either legacy single/dual-instance variables or the new NAMES/HOSTS/USERS/PASSWORDS arrays and log which mode is in use.
includes/downloaders/mover-tuning-start.sh
includes/downloaders/mover-tuning-end.sh
Fix and enhance script and config version comparison logic for both start and end scripts.
  • Adjust version comparison to correctly determine whether the local version is older or newer than the remote version without swallowing all errors.
  • Add logging when the local script/config version is newer than the remote, in addition to the existing upgrade-available path.
includes/downloaders/mover-tuning-start.sh
includes/downloaders/mover-tuning-end.sh
Migrate qBittorrent configuration from legacy single/dual-instance variables to array-based format in the default config file.
  • Replace QBIT_NAME_1/2, QBIT_HOST_1/2, QBIT_USER_1/2, QBIT_PASS_1/2 and ENABLE_QBIT_2 with NAMES, HOSTS, USERS, and PASSWORDS arrays, documenting that NAMES is optional and all arrays must be the same length.
  • Clarify DAYS_TO comment that it must be greater than or equal to DAYS_FROM and make a minor comment formatting tweak at the bottom of the file.
includes/downloaders/mover-tuning.cfg

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions github-actions bot added the Area: Backend Backend Changes, not related to a specific section label Nov 27, 2025
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The updated version checks in both scripts remove the previous || true guard on the grep pipeline; with set -euo pipefail enabled this will cause the whole script to exit when the local version is newer (i.e., when grep returns 1), so you should restore a non-fatal guard or explicitly handle the non-match case without relying on a failing pipeline.
  • In get_instance_details you concatenate instance fields with | and then split using IFS='|' read ...; this will break if any username or password ever contains |, so it would be safer to return fields via global variables, nameref parameters, or a small associative array instead of encoding them into a delimited string.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The updated version checks in both scripts remove the previous `|| true` guard on the `grep` pipeline; with `set -euo pipefail` enabled this will cause the whole script to exit when the local version is newer (i.e., when `grep` returns 1), so you should restore a non-fatal guard or explicitly handle the non-match case without relying on a failing pipeline.
- In `get_instance_details` you concatenate instance fields with `|` and then split using `IFS='|' read ...`; this will break if any username or password ever contains `|`, so it would be safer to return fields via global variables, nameref parameters, or a small associative array instead of encoding them into a delimited string.

## Individual Comments

### Comment 1
<location> `includes/downloaders/mover-tuning-start.sh:76-85` </location>
<code_context>
+    local format
+    format=$(detect_config_format)
+    
+    if [[ "$format" == "array" ]]; then
+        echo "${#HOSTS[@]}"
+    else
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Array-based config validates lengths but not that individual host/user/pass entries are non-empty.

This means a config with empty host/user/password values would still be accepted and only fail later at use time. Please add a per-index check in the array branch (e.g., loop over `i < ${#HOSTS[@]}`) to ensure `HOSTS[i]`, `USERS[i]`, and `PASSWORDS[i]` are all non-empty before proceeding.

Suggested implementation:

```
get_instance_count() {
    local format
    format=$(detect_config_format)

    if [[ "$format" == "array" ]]; then
        local i
        for (( i=0; i<${#HOSTS[@]}; i++ )); do
            if [[ -z "${HOSTS[i]}" || -z "${USERS[i]}" || -z "${PASSWORDS[i]}" ]]; then
                echo "Invalid array-based configuration: HOSTS[$i], USERS[$i], and PASSWORDS[$i] must all be non-empty." >&2
                exit 1
            fi
        done

        echo "${#HOSTS[@]}"

```

1. Ensure that `USERS` and `PASSWORDS` arrays are always defined when `HOSTS` is used in array mode; if there is any code path where they might be unset, they should be initialized (e.g., to empty arrays) before calling `get_instance_count`.
2. If `get_instance_count` is used in contexts where exiting the script is not desirable, you may want to refactor this validation into a separate function (e.g., `validate_array_config`) that callers can handle differently (e.g., by checking a return code) instead of exiting here.
</issue_to_address>

### Comment 2
<location> `includes/downloaders/mover-tuning-end.sh:63-72` </location>
<code_context>
+    local format
+    format=$(detect_config_format)
+    
+    if [[ "$format" == "array" ]]; then
+        echo "${#HOSTS[@]}"
+    else
</code_context>

<issue_to_address>
**suggestion:** Same array validation gap exists in the end script, leading to similar edge cases.

This mirrors the start script: it only enforces matching lengths for `HOSTS/USERS/PASSWORDS`, not that individual entries are non-empty. Once you add per-entry validation to the start script, please apply the same checks here so misconfigurations are caught consistently and early.

Suggested implementation:

```
    if [[ "$format" == "array" ]]; then
        # Validate that HOSTS, USERS, and PASSWORDS arrays are consistent and non-empty
        if (( ${#HOSTS[@]} == 0 )); then
            echo "No HOSTS configured for array-based configuration." >&2
            exit 1
        fi

        if (( ${#HOSTS[@]} != ${#USERS[@]} || ${#HOSTS[@]} != ${#PASSWORDS[@]} )); then
            echo "Configuration error: HOSTS, USERS, and PASSWORDS arrays must have the same length." >&2
            exit 1
        fi

        for i in "${!HOSTS[@]}"; do
            if [[ -z "${HOSTS[$i]}" || -z "${USERS[$i]}" || -z "${PASSWORDS[$i]}" ]]; then
                echo "Configuration error: HOSTS, USERS, and PASSWORDS entries must be non-empty (index $i)." >&2
                exit 1
            fi
        done

        echo "${#HOSTS[@]}"

```

1. The snippet you provided shows two `get_instance_count` definitions and a seemingly incomplete `if`/`fi` structure. In the actual file, ensure there is only one `get_instance_count` function and that its `if`/`else`/`fi` structure is syntactically correct.
2. If the start script uses specific error message wording or a shared helper for validation, you may want to adjust the error messages here to match that convention for consistency.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +63 to +72
if [[ "$format" == "array" ]]; then
echo "${#HOSTS[@]}"
else
# Legacy format: count based on ENABLE_QBIT_2
if [[ "${ENABLE_QBIT_2:-false}" == true ]]; then
echo "2"
else
echo "1"
fi
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Same array validation gap exists in the end script, leading to similar edge cases.

This mirrors the start script: it only enforces matching lengths for HOSTS/USERS/PASSWORDS, not that individual entries are non-empty. Once you add per-entry validation to the start script, please apply the same checks here so misconfigurations are caught consistently and early.

Suggested implementation:

    if [[ "$format" == "array" ]]; then
        # Validate that HOSTS, USERS, and PASSWORDS arrays are consistent and non-empty
        if (( ${#HOSTS[@]} == 0 )); then
            echo "No HOSTS configured for array-based configuration." >&2
            exit 1
        fi

        if (( ${#HOSTS[@]} != ${#USERS[@]} || ${#HOSTS[@]} != ${#PASSWORDS[@]} )); then
            echo "Configuration error: HOSTS, USERS, and PASSWORDS arrays must have the same length." >&2
            exit 1
        fi

        for i in "${!HOSTS[@]}"; do
            if [[ -z "${HOSTS[$i]}" || -z "${USERS[$i]}" || -z "${PASSWORDS[$i]}" ]]; then
                echo "Configuration error: HOSTS, USERS, and PASSWORDS entries must be non-empty (index $i)." >&2
                exit 1
            fi
        done

        echo "${#HOSTS[@]}"

  1. The snippet you provided shows two get_instance_count definitions and a seemingly incomplete if/fi structure. In the actual file, ensure there is only one get_instance_count function and that its if/else/fi structure is syntactically correct.
  2. If the start script uses specific error message wording or a shared helper for validation, you may want to adjust the error messages here to match that convention for consistency.

@github-actions
Copy link
Contributor

Deploying with ⚡ Cloudflare Pages

Latest commit:63f37380cdc156dffa20c2f0e145b56aad57e036
Status: ✅  Deploy successful!
Preview URL:https://43d434b9.trash-guides-6xu.pages.dev
Branch Preview URL:https://pr-2556.trash-guides-6xu.pages.dev

- Fixed the grep pipeline issue
- Fixed the pipe delimiter issue in get_instance_details
@github-actions
Copy link
Contributor

Deploying with ⚡ Cloudflare Pages

Latest commit:888fe37095a5fc37c6b97ca8a159db4595c5e1e8
Status: ✅  Deploy successful!
Preview URL:https://fa5cf38e.trash-guides-6xu.pages.dev
Branch Preview URL:https://pr-2556.trash-guides-6xu.pages.dev

@github-actions
Copy link
Contributor

Deploying with ⚡ Cloudflare Pages

Latest commit:9d86a26d0a36705f0dd67b1616659452fa7f8406
Status: ✅  Deploy successful!
Preview URL:https://1ec25ef1.trash-guides-6xu.pages.dev
Branch Preview URL:https://pr-2556.trash-guides-6xu.pages.dev

@github-actions
Copy link
Contributor

Deploying with ⚡ Cloudflare Pages

Latest commit:a71692337a8da010d7eaf6a48b8033706b40c741
Status: ✅  Deploy successful!
Preview URL:https://0330c660.trash-guides-6xu.pages.dev
Branch Preview URL:https://pr-2556.trash-guides-6xu.pages.dev

@TRaSH- TRaSH- changed the title Add support for unlimited number of qBittorrent instances feat(downloaders): Add support for unlimited numbers of qBt instances in the mover-tuner scripts Nov 29, 2025
@TRaSH-
Copy link
Contributor

TRaSH- commented Nov 29, 2025

@sourcery-ai review
@sourcery-ai guide

@github-actions
Copy link
Contributor

Deploying with ⚡ Cloudflare Pages

Latest commit:1b3815fb530a3718d65fe6ca6a8249baca652dad
Status: ✅  Deploy successful!
Preview URL:https://a9d9e862.trash-guides-6xu.pages.dev
Branch Preview URL:https://pr-2556.trash-guides-6xu.pages.dev

mover-tuning-start.sh:

Full validation with Unraid notifications for configuration errors
Shows detailed error messages with array lengths

mover-tuning-end.sh:

Basic validation (still checks for errors to prevent execution with bad config)
Only logs to terminal, no Unraid notifications
@github-actions
Copy link
Contributor

github-actions bot commented Dec 1, 2025

Deploying with ⚡ Cloudflare Pages

Latest commit:abe5a1853a9bf13bf36d85e09d51717ef57397d9
Status: ✅  Deploy successful!
Preview URL:https://d121a6cc.trash-guides-6xu.pages.dev
Branch Preview URL:https://pr-2556.trash-guides-6xu.pages.dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: Backend Backend Changes, not related to a specific section

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants