Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.10
FROM python:3.10-alpine

ADD . /home

Expand Down
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ Change your torrent client's download speed dynamically, on certain events such
This script is ideal for users with limited upload speed, however anyone can use it to maximise their upload speed, whilst keeping their Plex/Jellyfin/Emby streams buffer-free! Also great to adjust the download rate during the day, in case the bandwidth is needed for something else!



## Features
- Multi-server support for Plex, Jellyfin, Emby, and Tautulli.
- Supports qBittorrent and Transmission.
- Multi-torrent-client support.
- Bandwidth is split between them, by number of downloading/uploading torrents.
- Schedule a time/day when upload speed should be lowered.
- Support for unlimited speeds in schedules (equivalent to turning off speed limits).
- Stream-based speed control: Set different upload speeds based on the number of active streams instead of bandwidth usage.



## Setup
Expand Down Expand Up @@ -70,6 +74,95 @@ cd /boot/config/plugins/dockerMan/templates-user && touch my-speedrr.xml && nano
5. Run `python main.py --config_path config.yaml` to start.


## Stream-Based Speed Control

Instead of dynamically reducing upload speed based on bandwidth usage, you can configure speedrr to set specific upload speeds based on the number of active streams.

### Why Use Stream-Based Speeds?

Traditional bandwidth-based control is reactive—it reduces your upload speed based on how much bandwidth streams are using. Stream-based control is predictive**—you define exactly what upload speed you want for different numbers of streams.

Benefits:
- More predictable - You control exactly what happens with 1, 2, 3+ streams
- Max out when idle - Set unlimited upload when no streams are active
- Better balance - Fine-tune the trade-off between streaming quality and torrent upload
- Easier to configure - Just count streams instead of estimating bandwidth needs

### Quick Start

Add `stream_based_speeds` to your media server configuration:

```yaml
modules:
media_servers:
- type: plex
url: http://your-plex-server:32400
token: your_token
# ... other settings ...

stream_based_speeds:
enabled: true
speeds:
0: unlimited # No streams = unlimited upload
1: 10 # 1 stream = 10 Mbit/s upload
2: 8 # 2 streams = 8 Mbit/s upload
3: 6 # 3+ streams = 6 Mbit/s upload
default: 5 # Optional: fallback speed
```

### Configuration Options

`speeds` mapping - Define upload speeds for different stream counts:
- Numbers: `10`, `15`, `20` (in your configured units)
- Percentages: `"50%"`, `"80%"` (of max_upload)
- Unlimited: `unlimited` (removes speed limit)

`default` (optional) - Fallback speed for undefined stream counts. If omitted, uses the highest defined count's speed.

### How It Works

1. Stream Counting: Speedrr monitors your media server and counts active streams
2. Filtering: Local streams, paused streams, and ignored IPs are excluded from the count
3. Speed Selection: Upload speed is set based on your configured mapping
4. Dynamic Updates: Speed adjusts automatically as streams start/stop

### Use Cases

Scenario 1: Maximize seeding when idle
```yaml
speeds:
0: unlimited # Full upload when not streaming
1: 8 # Conservative when streaming
```

Scenario 2: Granular control for multiple users
```yaml
speeds:
0: unlimited
1: 12 # One user streaming
2: 10 # Two users
3: 8 # Three users
4: 6 # Four or more users
```

Scenario 3: Using percentages
```yaml
speeds:
0: "100%" # Full max_upload
1: "70%" # 70% of max_upload
2: "50%" # 50% of max_upload
```

### Complete Example

See [`config.stream_based.example.yaml`](config.stream_based.example.yaml) for a fully documented configuration with detailed comments and multiple examples.

### Backward Compatibility

This feature is completely optional. Existing configurations without `stream_based_speeds` will continue to work with the traditional bandwidth-based speed control.



## Contributing
Anyone is welcome to contribute! Feel free to open pull requests.

Expand Down
24 changes: 16 additions & 8 deletions clients/qbittorrent.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,24 @@ def get_active_torrent_count(self) -> int:
def set_upload_speed(self, speed: Union[int, float]) -> None:
"Set the upload speed limit for the client, in config units."

logger.debug(f"<qbit|{self._client_config.url}> Setting upload speed to {speed}{self._config.units}")
self._client.transfer_set_upload_limit(
max(1, int(bit_conv(speed, self._config.units, 'B')))
)
if speed == float('inf'):
logger.debug(f"<qbit|{self._client_config.url}> Setting upload speed to unlimited")
self._client.transfer_set_upload_limit(0)
else:
logger.debug(f"<qbit|{self._client_config.url}> Setting upload speed to {speed}{self._config.units}")
self._client.transfer_set_upload_limit(
max(1, int(bit_conv(speed, self._config.units, 'B')))
)


def set_download_speed(self, speed: Union[int, float]) -> None:
"Set the download speed limit for the client, in config units."

logger.debug(f"<qbit|{self._client_config.url}> Setting dowload speed to {speed}{self._config.units}")
self._client.transfer_set_download_limit(
max(1, int(bit_conv(speed, self._config.units, 'B')))
)
if speed == float('inf'):
logger.debug(f"<qbit|{self._client_config.url}> Setting download speed to unlimited")
self._client.transfer_set_download_limit(0)
else:
logger.debug(f"<qbit|{self._client_config.url}> Setting dowload speed to {speed}{self._config.units}")
self._client.transfer_set_download_limit(
max(1, int(bit_conv(speed, self._config.units, 'B')))
)
22 changes: 14 additions & 8 deletions clients/transmission.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,21 @@ def get_active_torrent_count(self) -> int:
def set_upload_speed(self, speed: Union[int, float]) -> None:
"Set the upload speed limit for the client, in config units."

logger.debug(f"<trans|{self._client_config.url}> Setting upload speed to {speed}{self._config.units}")

speed_limit_up = max(1, int(bit_conv(speed, self._config.units, 'KB')))
self._client.set_session(speed_limit_up=speed_limit_up)
if speed == float('inf'):
logger.debug(f"<trans|{self._client_config.url}> Setting upload speed to unlimited")
self._client.set_session(speed_limit_up_enabled=False)
else:
logger.debug(f"<trans|{self._client_config.url}> Setting upload speed to {speed}{self._config.units}")
speed_limit_up = max(1, int(bit_conv(speed, self._config.units, 'KB')))
self._client.set_session(speed_limit_up_enabled=True, speed_limit_up=speed_limit_up)

def set_download_speed(self, speed: Union[int, float]) -> None:
"Set the download speed limit for the client, in config units."

logger.debug(f"<trans|{self._client_config.url}> Setting dowload speed to {speed}{self._config.units}")

speed_limit_down = max(1, int(bit_conv(speed, self._config.units, "KB")))
self._client.set_session(speed_limit_down=speed_limit_down)
if speed == float('inf'):
logger.debug(f"<trans|{self._client_config.url}> Setting download speed to unlimited")
self._client.set_session(speed_limit_down_enabled=False)
else:
logger.debug(f"<trans|{self._client_config.url}> Setting dowload speed to {speed}{self._config.units}")
speed_limit_down = max(1, int(bit_conv(speed, self._config.units, "KB")))
self._client.set_session(speed_limit_down_enabled=True, speed_limit_down=speed_limit_down)
Loading
Loading