diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml
deleted file mode 100644
index 036d6ece..00000000
--- a/.github/workflows/backend-tests.yml
+++ /dev/null
@@ -1,161 +0,0 @@
-name: Backend Tests
-
-on:
- push:
- branches: [main, dev]
- paths:
- - 'apps/**'
- - 'core/**'
- - 'tests/**'
- - 'dispatcharr/**'
- - 'pyproject.toml'
- - 'version.py'
- - 'manage.py'
- - 'scripts/ci_backend_test_labels.py'
- - 'scripts/ci_bootstrap_backend.sh'
- - '.github/workflows/backend-tests.yml'
- pull_request:
- branches: [main, dev]
- paths:
- - 'apps/**'
- - 'core/**'
- - 'tests/**'
- - 'dispatcharr/**'
- - 'pyproject.toml'
- - 'version.py'
- - 'manage.py'
- - 'scripts/ci_backend_test_labels.py'
- - 'scripts/ci_bootstrap_backend.sh'
- - '.github/workflows/backend-tests.yml'
- workflow_dispatch:
- inputs:
- full_suite:
- description: Run the full backend test suite
- type: boolean
- default: true
-
-permissions:
- contents: read
- packages: read
-
-concurrency:
- group: backend-tests-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- plan:
- name: Plan test groups
- runs-on: ubuntu-latest
- outputs:
- labels: ${{ steps.resolve.outputs.labels }}
- has_tests: ${{ steps.resolve.outputs.has_tests }}
- base_image: ${{ steps.base_image.outputs.image }}
- sync_python_deps: ${{ steps.base_image.outputs.sync_python_deps }}
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
- with:
- fetch-depth: 0
-
- - name: Collect changed paths
- id: changed
- shell: bash
- run: |
- set -euo pipefail
- : > /tmp/changed_paths.txt
-
- if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
- if [ "${{ inputs.full_suite }}" = "true" ]; then
- : > /tmp/changed_paths.txt
- echo "mode=full" >> "$GITHUB_OUTPUT"
- else
- git diff --name-only HEAD~1 HEAD > /tmp/changed_paths.txt || true
- echo "mode=diff" >> "$GITHUB_OUTPUT"
- fi
- elif [ "${{ github.event_name }}" = "pull_request" ]; then
- git fetch origin "${{ github.base_ref }}"
- git diff --name-only "origin/${{ github.base_ref }}...HEAD" > /tmp/changed_paths.txt
- echo "mode=pr" >> "$GITHUB_OUTPUT"
- else
- if [ "${{ github.event.before }}" = "0000000000000000000000000000000000000000" ]; then
- git ls-files > /tmp/changed_paths.txt
- echo "mode=initial-push" >> "$GITHUB_OUTPUT"
- else
- git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" > /tmp/changed_paths.txt
- echo "mode=push" >> "$GITHUB_OUTPUT"
- fi
- fi
-
- - name: Select base image
- id: base_image
- shell: bash
- run: |
- set -euo pipefail
- if [ "${{ github.event_name }}" = "pull_request" ]; then
- TARGET_BRANCH="${{ github.base_ref }}"
- else
- TARGET_BRANCH="${{ github.ref_name }}"
- fi
- if [ "$TARGET_BRANCH" = "main" ]; then
- TAG="base"
- else
- TAG="base-dev"
- fi
- REPO_OWNER="$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')"
- REPO_NAME="$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]')"
- echo "image=ghcr.io/${REPO_OWNER}/${REPO_NAME}:${TAG}" >> "$GITHUB_OUTPUT"
- if grep -qx 'pyproject.toml' /tmp/changed_paths.txt || [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
- echo "sync_python_deps=true" >> "$GITHUB_OUTPUT"
- else
- echo "sync_python_deps=false" >> "$GITHUB_OUTPUT"
- fi
- echo "Using base image: ghcr.io/${REPO_OWNER}/${REPO_NAME}:${TAG}"
-
- - name: Resolve Django test labels
- id: resolve
- env:
- FULL_SUITE: ${{ github.event_name == 'workflow_dispatch' && inputs.full_suite == true && 'true' || 'false' }}
- run: |
- set -euo pipefail
- LABELS=$(python scripts/ci_backend_test_labels.py < /tmp/changed_paths.txt)
- echo "labels=${LABELS}" >> "$GITHUB_OUTPUT"
- if [ "${LABELS}" = "[]" ]; then
- echo "has_tests=false" >> "$GITHUB_OUTPUT"
- else
- echo "has_tests=true" >> "$GITHUB_OUTPUT"
- fi
- echo "Selected labels: ${LABELS}"
-
- test:
- name: ${{ matrix.label }}
- needs: plan
- if: needs.plan.outputs.has_tests == 'true'
- runs-on: ubuntu-latest
- container:
- image: ${{ needs.plan.outputs.base_image }}
- credentials:
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
- options: --entrypoint ""
- strategy:
- max-parallel: 6
- fail-fast: false
- matrix:
- label: ${{ fromJSON(needs.plan.outputs.labels) }}
- env:
- DISPATCHARR_ENV: aio
- DJANGO_SECRET_KEY: ci-test-secret-key
- POSTGRES_DB: dispatcharr
- POSTGRES_USER: dispatch
- POSTGRES_PASSWORD: secret
- DISPATCHARR_LOG_LEVEL: WARNING
- SYNC_PYTHON_DEPS: ${{ needs.plan.outputs.sync_python_deps }}
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
-
- - name: Run tests in base image
- env:
- GITHUB_WORKSPACE: ${{ github.workspace }}
- run: bash scripts/ci_bootstrap_backend.sh "${{ matrix.label }}" -v2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a97f5158..ea23e1dd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,21 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
-## [0.27.2] - 2026-06-30
-
-### Added
-
-- **New proxy setting: Client Connect Grace Period (`channel_client_wait_period`, default 5s).** Adds a dedicated timeout for channels that have filled their buffer but still have no viewers (`waiting_for_clients`). Previously that window reused `channel_shutdown_delay` (default 0s), so those channels were torn down almost immediately.
-
-### Changed
-
-- **Proxy grace-period settings are now split into three distinct timeouts.** The cleanup watchdog already applied `channel_init_grace_period` while a channel was still connecting (buffer not ready) and reused `channel_shutdown_delay` once `connection_ready_time` was set, including for `waiting_for_clients` with zero viewers. With the default `channel_shutdown_delay` of 0s, a buffered channel waiting for its first viewer was stopped almost immediately; raising shutdown delay was the only workaround, but that also delayed teardown after real disconnects. Behaviour is now:
- - **`channel_init_grace_period` (default 60s, max 300s):** how long the proxy may spend connecting and cycling failover streams before giving up on startup.
- - **`channel_client_wait_period` (default 5s):** how long a ready channel with no viewers stays up waiting for the first client (the original grace-period use case).
- - **`channel_shutdown_delay` (default 0s):** delay after the last client disconnects only; no longer applies when the buffer is ready but no viewer has connected yet.
-- **Migration 0026 bumps `channel_init_grace_period` to 60s when the stored value is below 60.** Existing installs on the old 5s default (or any custom value under 60) are raised automatically. Values already at 60s or higher are unchanged. If you previously raised `channel_shutdown_delay` to keep buffered channels alive with no viewers, set `channel_client_wait_period` instead (Settings → Proxy → Advanced).
-- **Proxy settings UI: less-used options moved under Advanced.** Settings → Proxy now shows the day-to-day tuning fields by default (`buffering_timeout`, `buffering_speed`, `channel_shutdown_delay`, `new_client_behind_seconds`). **Buffer Chunk TTL**, **Channel Initialization Timeout**, and **Client Connect Grace Period** are tucked under **Show Advanced Settings**.
-
## [0.27.1] - 2026-06-25
### Security
diff --git a/apps/channels/tests/test_ts_proxy_teardown.py b/apps/channels/tests/test_ts_proxy_teardown.py
index b76918ac..ef78e576 100644
--- a/apps/channels/tests/test_ts_proxy_teardown.py
+++ b/apps/channels/tests/test_ts_proxy_teardown.py
@@ -923,62 +923,3 @@ class StreamManagerStillOwnerTests(TestCase):
return_value=5,
):
self.assertTrue(manager._still_owner())
-
-
-class PreActiveNoClientsTimeoutTests(TestCase):
- @patch("apps.proxy.live_proxy.server.ConfigHelper.channel_client_wait_period", return_value=5)
- def test_buffer_ready_uses_client_wait_period(self, _mock_client_wait):
- should_stop, timeout, reason = ProxyServer._pre_active_no_clients_should_stop(
- connection_ready_time=1000.0,
- start_time=900.0,
- now=1006.0,
- )
- self.assertTrue(should_stop)
- self.assertEqual(timeout, 5)
- self.assertEqual(reason, "client_wait")
-
- @patch("apps.proxy.live_proxy.server.ConfigHelper.channel_client_wait_period", return_value=5)
- def test_buffer_ready_within_client_wait_period(self, _mock_client_wait):
- should_stop, timeout, reason = ProxyServer._pre_active_no_clients_should_stop(
- connection_ready_time=1000.0,
- start_time=900.0,
- now=1003.0,
- )
- self.assertFalse(should_stop)
- self.assertEqual(timeout, 5)
- self.assertEqual(reason, "client_wait")
-
- @patch("apps.proxy.live_proxy.server.ConfigHelper.channel_init_grace_period", return_value=60)
- def test_startup_uses_init_grace_period(self, _mock_init_grace):
- should_stop, timeout, reason = ProxyServer._pre_active_no_clients_should_stop(
- connection_ready_time=None,
- start_time=1000.0,
- now=1070.0,
- )
- self.assertTrue(should_stop)
- self.assertEqual(timeout, 60)
- self.assertEqual(reason, "startup")
-
- @patch("apps.proxy.live_proxy.server.ConfigHelper.channel_init_grace_period", return_value=60)
- def test_startup_within_init_grace_period(self, _mock_init_grace):
- should_stop, timeout, reason = ProxyServer._pre_active_no_clients_should_stop(
- connection_ready_time=None,
- start_time=1000.0,
- now=1030.0,
- )
- self.assertFalse(should_stop)
- self.assertEqual(timeout, 60)
- self.assertEqual(reason, "startup")
-
- @patch("apps.proxy.live_proxy.server.ConfigHelper.channel_shutdown_delay", return_value=30)
- @patch("apps.proxy.live_proxy.server.ConfigHelper.channel_client_wait_period", return_value=5)
- def test_buffer_ready_does_not_use_shutdown_delay(self, mock_client_wait, mock_shutdown_delay):
- should_stop, _, reason = ProxyServer._pre_active_no_clients_should_stop(
- connection_ready_time=1000.0,
- start_time=900.0,
- now=1006.0,
- )
- self.assertTrue(should_stop)
- self.assertEqual(reason, "client_wait")
- mock_client_wait.assert_called_once()
- mock_shutdown_delay.assert_not_called()
diff --git a/apps/proxy/config.py b/apps/proxy/config.py
index 48d9fc9b..c7ba5628 100644
--- a/apps/proxy/config.py
+++ b/apps/proxy/config.py
@@ -42,8 +42,7 @@ class BaseConfig:
"buffering_speed": 1.0,
"redis_chunk_ttl": 60,
"channel_shutdown_delay": 0,
- "channel_init_grace_period": 60,
- "channel_client_wait_period": 5,
+ "channel_init_grace_period": 5,
"new_client_behind_seconds": 5,
}
@@ -136,13 +135,7 @@ class TSConfig(BaseConfig):
def get_channel_init_grace_period(cls):
"""Max seconds to wait for initial buffer fill during channel startup."""
settings = cls.get_proxy_settings()
- return settings.get("channel_init_grace_period", 60)
-
- @classmethod
- def get_channel_client_wait_period(cls):
- """Seconds to keep a ready channel alive waiting for the first client to connect."""
- settings = cls.get_proxy_settings()
- return settings.get("channel_client_wait_period", 5)
+ return settings.get("channel_init_grace_period", 5)
# Dynamic property access for these settings
@property
@@ -161,6 +154,5 @@ class TSConfig(BaseConfig):
def CHANNEL_INIT_GRACE_PERIOD(self):
return self.get_channel_init_grace_period()
- @property
- def CHANNEL_CLIENT_WAIT_PERIOD(self):
- return self.get_channel_client_wait_period()
+
+
diff --git a/apps/proxy/live_proxy/config_helper.py b/apps/proxy/live_proxy/config_helper.py
index 846b0153..d151a8a8 100644
--- a/apps/proxy/live_proxy/config_helper.py
+++ b/apps/proxy/live_proxy/config_helper.py
@@ -110,11 +110,6 @@ class ConfigHelper:
"""Max seconds to wait for initial buffer fill during channel startup."""
return Config.get_channel_init_grace_period()
- @staticmethod
- def channel_client_wait_period():
- """Seconds to keep a ready channel alive waiting for the first client to connect."""
- return Config.get_channel_client_wait_period()
-
@staticmethod
def chunk_timeout():
"""
diff --git a/apps/proxy/live_proxy/server.py b/apps/proxy/live_proxy/server.py
index 97c5b68b..fd54fc24 100644
--- a/apps/proxy/live_proxy/server.py
+++ b/apps/proxy/live_proxy/server.py
@@ -910,26 +910,6 @@ class ProxyServer:
delay = ConfigHelper.channel_shutdown_delay()
return max(int(delay * 2), 60)
- @staticmethod
- def _pre_active_no_clients_should_stop(connection_ready_time, start_time, now=None):
- """
- Decide whether a pre-active channel with zero clients should be stopped.
-
- Returns (should_stop, timeout_seconds, reason) where reason is
- 'client_wait' (buffer ready, waiting for first viewer) or 'startup'
- (still connecting / filling buffer).
- """
- now = now if now is not None else time.time()
- if connection_ready_time:
- elapsed = now - connection_ready_time
- timeout = ConfigHelper.channel_client_wait_period()
- return elapsed > timeout, timeout, "client_wait"
- if start_time:
- elapsed = now - start_time
- timeout = ConfigHelper.channel_init_grace_period()
- return elapsed > timeout, timeout, "startup"
- return False, None, None
-
def _wait_for_shutdown_delay(self, channel_id):
"""
Wait until shutdown_delay has elapsed since the Redis disconnect
@@ -1819,27 +1799,31 @@ class ProxyServer:
start_time = connection_attempt_time or init_time
if start_time:
- should_stop, timeout, reason = (
- self._pre_active_no_clients_should_stop(
- connection_ready_time,
- start_time,
- )
- )
- if should_stop:
- if reason == "client_wait":
- time_since_ready = time.time() - connection_ready_time
+ # Check which timeout to apply based on channel lifecycle
+ if connection_ready_time:
+ # Already reached ready - use shutdown_delay
+ time_since_ready = time.time() - connection_ready_time
+ shutdown_delay = ConfigHelper.channel_shutdown_delay()
+
+ if time_since_ready > shutdown_delay:
logger.warning(
f"Channel {channel_id} in {channel_state} state with 0 clients for {time_since_ready:.1f}s "
- f"(buffer ready, no client connected, client_wait_period: {timeout}s) - stopping channel"
+ f"(after reaching ready, shutdown_delay: {shutdown_delay}s) - stopping channel"
)
- else:
- time_since_start = time.time() - start_time
+ self._coordinated_stop_channel(channel_id)
+ continue
+ else:
+ # Never reached ready - use grace_period timeout
+ time_since_start = time.time() - start_time
+ connecting_timeout = ConfigHelper.channel_init_grace_period()
+
+ if time_since_start > connecting_timeout:
logger.warning(
f"Channel {channel_id} stuck in {channel_state} state for {time_since_start:.1f}s "
- f"with no clients (timeout: {timeout}s) - stopping channel due to upstream issues"
+ f"with no clients (timeout: {connecting_timeout}s) - stopping channel due to upstream issues"
)
- self._coordinated_stop_channel(channel_id)
- continue
+ self._coordinated_stop_channel(channel_id)
+ continue
elif (
channel_state == ChannelState.WAITING_FOR_CLIENTS
and total_clients > 0
diff --git a/apps/proxy/live_proxy/tests/test_proxy_settings.py b/apps/proxy/live_proxy/tests/test_proxy_settings.py
deleted file mode 100644
index e821f1cb..00000000
--- a/apps/proxy/live_proxy/tests/test_proxy_settings.py
+++ /dev/null
@@ -1,143 +0,0 @@
-"""Tests for proxy settings defaults, serializer validation, and migration 0026."""
-
-from importlib import import_module
-from unittest.mock import patch
-
-from django.apps import apps
-from django.test import SimpleTestCase, TestCase
-
-from apps.proxy.config import TSConfig
-from core.models import CoreSettings
-from core.serializers import ProxySettingsSerializer
-
-MIGRATION_0026 = import_module("core.migrations.0026_add_channel_client_wait_period")
-
-
-class TSConfigProxySettingsDefaultsTests(SimpleTestCase):
- @patch.object(TSConfig, "get_proxy_settings", return_value={})
- def test_channel_init_grace_period_default(self, _mock_settings):
- self.assertEqual(TSConfig.get_channel_init_grace_period(), 60)
-
- @patch.object(TSConfig, "get_proxy_settings", return_value={})
- def test_channel_client_wait_period_default(self, _mock_settings):
- self.assertEqual(TSConfig.get_channel_client_wait_period(), 5)
-
- @patch.object(
- TSConfig,
- "get_proxy_settings",
- return_value={
- "channel_init_grace_period": 120,
- "channel_client_wait_period": 15,
- },
- )
- def test_settings_override_db_values(self, _mock_settings):
- self.assertEqual(TSConfig.get_channel_init_grace_period(), 120)
- self.assertEqual(TSConfig.get_channel_client_wait_period(), 15)
-
-
-class ProxySettingsSerializerTests(SimpleTestCase):
- def _valid_payload(self, **overrides):
- payload = {
- "buffering_timeout": 15,
- "buffering_speed": 1.0,
- "redis_chunk_ttl": 60,
- "channel_shutdown_delay": 0,
- "channel_init_grace_period": 60,
- "channel_client_wait_period": 5,
- "new_client_behind_seconds": 5,
- }
- payload.update(overrides)
- return payload
-
- def test_accepts_new_client_wait_period(self):
- serializer = ProxySettingsSerializer(data=self._valid_payload())
- self.assertTrue(serializer.is_valid(), serializer.errors)
- self.assertEqual(serializer.validated_data["channel_client_wait_period"], 5)
-
- def test_init_grace_period_allows_up_to_300(self):
- serializer = ProxySettingsSerializer(
- data=self._valid_payload(channel_init_grace_period=300)
- )
- self.assertTrue(serializer.is_valid(), serializer.errors)
-
- def test_init_grace_period_rejects_above_300(self):
- serializer = ProxySettingsSerializer(
- data=self._valid_payload(channel_init_grace_period=301)
- )
- self.assertFalse(serializer.is_valid())
- self.assertIn("channel_init_grace_period", serializer.errors)
-
-
-class CoreSettingsProxyDefaultsTests(TestCase):
- def test_get_proxy_settings_defaults_when_missing(self):
- CoreSettings.objects.filter(key="proxy_settings").delete()
- defaults = CoreSettings.get_proxy_settings()
- self.assertEqual(defaults["channel_init_grace_period"], 60)
- self.assertEqual(defaults["channel_client_wait_period"], 5)
-
-
-class Migration0026ProxySettingsTests(TestCase):
- def _run_migration_forward(self):
- MIGRATION_0026.add_channel_client_wait_period(apps, None)
-
- def _set_proxy_settings(self, value):
- settings_obj, _ = CoreSettings.objects.get_or_create(
- key="proxy_settings",
- defaults={"name": "Proxy Settings", "value": value},
- )
- settings_obj.value = value
- settings_obj.save(update_fields=["value"])
- return settings_obj
-
- def test_bumps_legacy_init_grace_and_adds_client_wait(self):
- settings_obj = self._set_proxy_settings(
- {
- "buffering_timeout": 15,
- "buffering_speed": 1.0,
- "redis_chunk_ttl": 60,
- "channel_shutdown_delay": 0,
- "channel_init_grace_period": 5,
- "new_client_behind_seconds": 5,
- }
- )
-
- self._run_migration_forward()
- settings_obj.refresh_from_db()
-
- self.assertEqual(settings_obj.value["channel_init_grace_period"], 60)
- self.assertEqual(settings_obj.value["channel_client_wait_period"], 5)
-
- def test_bumps_init_grace_below_new_default(self):
- settings_obj = self._set_proxy_settings(
- {
- "buffering_timeout": 15,
- "buffering_speed": 1.0,
- "redis_chunk_ttl": 60,
- "channel_shutdown_delay": 0,
- "channel_init_grace_period": 45,
- "new_client_behind_seconds": 5,
- }
- )
-
- self._run_migration_forward()
- settings_obj.refresh_from_db()
-
- self.assertEqual(settings_obj.value["channel_init_grace_period"], 60)
-
- def test_preserves_init_grace_at_or_above_new_default(self):
- settings_obj = self._set_proxy_settings(
- {
- "buffering_timeout": 15,
- "buffering_speed": 1.0,
- "redis_chunk_ttl": 60,
- "channel_shutdown_delay": 0,
- "channel_init_grace_period": 90,
- "new_client_behind_seconds": 5,
- }
- )
-
- self._run_migration_forward()
- settings_obj.refresh_from_db()
-
- self.assertEqual(settings_obj.value["channel_init_grace_period"], 90)
- self.assertEqual(settings_obj.value["channel_client_wait_period"], 5)
diff --git a/core/api_views.py b/core/api_views.py
index ee7d1db1..ec66d69a 100644
--- a/core/api_views.py
+++ b/core/api_views.py
@@ -225,8 +225,7 @@ class ProxySettingsViewSet(viewsets.ViewSet):
"buffering_speed": 1.0,
"redis_chunk_ttl": 60,
"channel_shutdown_delay": 0,
- "channel_init_grace_period": 60,
- "channel_client_wait_period": 5,
+ "channel_init_grace_period": 5,
"new_client_behind_seconds": 5,
}
settings_obj, created = CoreSettings.objects.get_or_create(
diff --git a/core/migrations/0026_add_channel_client_wait_period.py b/core/migrations/0026_add_channel_client_wait_period.py
deleted file mode 100644
index 6119eb66..00000000
--- a/core/migrations/0026_add_channel_client_wait_period.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from django.db import migrations
-
-PROXY_SETTINGS_KEY = "proxy_settings"
-
-
-def add_channel_client_wait_period(apps, schema_editor):
- CoreSettings = apps.get_model("core", "CoreSettings")
-
- try:
- obj = CoreSettings.objects.get(key=PROXY_SETTINGS_KEY)
- except CoreSettings.DoesNotExist:
- return
-
- value = obj.value if isinstance(obj.value, dict) else {}
-
- # Add the new client-connect grace period default.
- value.setdefault("channel_client_wait_period", 5)
-
- # channel_init_grace_period was repurposed in 0.27.1 as the channel startup
- # timeout (replacing a hardcoded 10s). Values below the new 60s default are
- # too short when a channel has many failover streams to cycle through.
- current_init = value.get("channel_init_grace_period", 5)
- if current_init < 60:
- value["channel_init_grace_period"] = 60
-
- obj.value = value
- obj.save()
-
-
-def remove_channel_client_wait_period(apps, schema_editor):
- CoreSettings = apps.get_model("core", "CoreSettings")
-
- try:
- obj = CoreSettings.objects.get(key=PROXY_SETTINGS_KEY)
- except CoreSettings.DoesNotExist:
- return
-
- value = obj.value if isinstance(obj.value, dict) else {}
- value.pop("channel_client_wait_period", None)
- obj.value = value
- obj.save()
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("core", "0025_move_preferred_region_and_auto_import_to_system_settings"),
- ]
-
- operations = [
- migrations.RunPython(add_channel_client_wait_period, remove_channel_client_wait_period),
- ]
diff --git a/core/models.py b/core/models.py
index ad9677c6..60be2f0e 100644
--- a/core/models.py
+++ b/core/models.py
@@ -394,8 +394,7 @@ class CoreSettings(models.Model):
"buffering_speed": 1.0,
"redis_chunk_ttl": 60,
"channel_shutdown_delay": 0,
- "channel_init_grace_period": 60,
- "channel_client_wait_period": 5,
+ "channel_init_grace_period": 5,
"new_client_behind_seconds": 5,
})
diff --git a/core/serializers.py b/core/serializers.py
index 1398b194..a0f27baf 100644
--- a/core/serializers.py
+++ b/core/serializers.py
@@ -96,8 +96,7 @@ class ProxySettingsSerializer(serializers.Serializer):
buffering_speed = serializers.FloatField(min_value=0.1, max_value=10.0)
redis_chunk_ttl = serializers.IntegerField(min_value=10, max_value=3600)
channel_shutdown_delay = serializers.IntegerField(min_value=0, max_value=300)
- channel_init_grace_period = serializers.IntegerField(min_value=0, max_value=300)
- channel_client_wait_period = serializers.IntegerField(min_value=0, max_value=300, required=False, default=5)
+ channel_init_grace_period = serializers.IntegerField(min_value=0, max_value=60)
new_client_behind_seconds = serializers.IntegerField(min_value=0, max_value=120, required=False, default=5)
def validate_buffering_timeout(self, value):
@@ -121,16 +120,9 @@ class ProxySettingsSerializer(serializers.Serializer):
return value
def validate_channel_init_grace_period(self, value):
- if value < 0 or value > 300:
+ if value < 0 or value > 60:
raise serializers.ValidationError(
- "Channel initialization timeout must be between 0 and 300 seconds"
- )
- return value
-
- def validate_channel_client_wait_period(self, value):
- if value < 0 or value > 300:
- raise serializers.ValidationError(
- "Client connect grace period must be between 0 and 300 seconds"
+ "Channel initialization timeout must be between 0 and 60 seconds"
)
return value
diff --git a/frontend/src/components/forms/settings/ProxySettingsForm.jsx b/frontend/src/components/forms/settings/ProxySettingsForm.jsx
index 91b36bb7..52440769 100644
--- a/frontend/src/components/forms/settings/ProxySettingsForm.jsx
+++ b/frontend/src/components/forms/settings/ProxySettingsForm.jsx
@@ -5,119 +5,80 @@ import { updateSetting } from '../../../utils/pages/SettingsUtils.js';
import {
Alert,
Button,
- Collapse,
Flex,
NumberInput,
Stack,
TextInput,
} from '@mantine/core';
-import { ChevronDown, ChevronRight } from 'lucide-react';
import { PROXY_SETTINGS_OPTIONS } from '../../../constants.js';
import {
getProxySettingDefaults,
getProxySettingsFormInitialValues,
} from '../../../utils/forms/settings/ProxySettingsFormUtils.js';
-const isNumericField = (key) => {
- return [
- 'buffering_timeout',
- 'redis_chunk_ttl',
- 'channel_shutdown_delay',
- 'channel_init_grace_period',
- 'channel_client_wait_period',
- 'new_client_behind_seconds',
- ].includes(key);
-};
-
-const isFloatField = (key) => key === 'buffering_speed';
-
-const getNumericFieldMax = (key) => {
- if (key === 'buffering_timeout') return 300;
- if (key === 'redis_chunk_ttl') return 3600;
- if (key === 'channel_shutdown_delay') return 300;
- if (key === 'channel_client_wait_period') return 300;
- if (key === 'new_client_behind_seconds') return 120;
- return 300;
-};
-
-const renderProxySettingField = (key, config, proxySettingsForm) => {
- if (isNumericField(key)) {
- return (
-