Add configurable Soulseek search timeout and buffer
Introduces 'search_timeout' and 'search_timeout_buffer' options to Soulseek settings in the config, backend, and web UI. The backend now uses these values to control search duration and polling, allowing users to fine-tune how long searches run and how long to wait for late results.
This commit is contained in:
parent
a2bdb93498
commit
5d29131530
4 changed files with 42 additions and 10 deletions
|
|
@ -30,7 +30,9 @@
|
||||||
"slskd_url": "http://host.docker.internal:5030",
|
"slskd_url": "http://host.docker.internal:5030",
|
||||||
"api_key": "SoulseekAPIKey",
|
"api_key": "SoulseekAPIKey",
|
||||||
"download_path": "/app/downloads",
|
"download_path": "/app/downloads",
|
||||||
"transfer_path": "/app/Transfer"
|
"transfer_path": "/app/Transfer",
|
||||||
|
"search_timeout": 60,
|
||||||
|
"search_timeout_buffer": 15
|
||||||
},
|
},
|
||||||
"logging": {
|
"logging": {
|
||||||
"path": "logs/app.log",
|
"path": "logs/app.log",
|
||||||
|
|
|
||||||
|
|
@ -602,17 +602,22 @@ class SoulseekClient:
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def search(self, query: str, timeout: int = 60, progress_callback=None) -> tuple[List[TrackResult], List[AlbumResult]]:
|
async def search(self, query: str, timeout: int = None, progress_callback=None) -> tuple[List[TrackResult], List[AlbumResult]]:
|
||||||
if not self.base_url:
|
if not self.base_url:
|
||||||
logger.error("Soulseek client not configured")
|
logger.error("Soulseek client not configured")
|
||||||
return [], []
|
return [], []
|
||||||
|
|
||||||
|
# Get timeout from config if not specified
|
||||||
|
if timeout is None:
|
||||||
|
from config.settings import config_manager
|
||||||
|
timeout = config_manager.get('soulseek.search_timeout', 60)
|
||||||
|
|
||||||
# Apply rate limiting before search
|
# Apply rate limiting before search
|
||||||
await self._wait_for_rate_limit()
|
await self._wait_for_rate_limit()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"Starting search for: '{query}'")
|
logger.info(f"Starting search for: '{query}' (slskd timeout: {timeout}s)")
|
||||||
|
|
||||||
search_data = {
|
search_data = {
|
||||||
'searchText': query,
|
'searchText': query,
|
||||||
'timeout': timeout * 1000, # slskd expects milliseconds
|
'timeout': timeout * 1000, # slskd expects milliseconds
|
||||||
|
|
@ -645,13 +650,24 @@ class SoulseekClient:
|
||||||
|
|
||||||
# Track this search as active
|
# Track this search as active
|
||||||
self.active_searches[search_id] = True
|
self.active_searches[search_id] = True
|
||||||
|
|
||||||
|
# Get timeout buffer from config
|
||||||
|
from config.settings import config_manager
|
||||||
|
timeout_buffer = config_manager.get('soulseek.search_timeout_buffer', 15)
|
||||||
|
|
||||||
# Poll for results - process and emit results immediately when found
|
# Poll for results - process and emit results immediately when found
|
||||||
all_responses = []
|
all_responses = []
|
||||||
all_tracks = []
|
all_tracks = []
|
||||||
all_albums = []
|
all_albums = []
|
||||||
poll_interval = 1 # Check every 1.5 seconds for more responsive updates
|
poll_interval = 1 # Check every 1 second for responsive updates
|
||||||
max_polls = int(timeout / poll_interval) # 20 attempts over 30 seconds
|
|
||||||
|
# IMPORTANT: Poll for LONGER than slskd searches to catch all results
|
||||||
|
# slskd timeout: how long slskd searches for
|
||||||
|
# polling timeout: how long WE wait for slskd to finish (with buffer)
|
||||||
|
polling_timeout = timeout + timeout_buffer
|
||||||
|
max_polls = int(polling_timeout / poll_interval)
|
||||||
|
|
||||||
|
logger.info(f"Polling for up to {polling_timeout}s (slskd timeout: {timeout}s + buffer: {timeout_buffer}s)")
|
||||||
|
|
||||||
for poll_count in range(max_polls):
|
for poll_count in range(max_polls):
|
||||||
# Check if search was cancelled
|
# Check if search was cancelled
|
||||||
|
|
|
||||||
|
|
@ -2273,6 +2273,16 @@
|
||||||
<label>API Key:</label>
|
<label>API Key:</label>
|
||||||
<input type="password" id="soulseek-api-key" placeholder="Slskd API Key">
|
<input type="password" id="soulseek-api-key" placeholder="Slskd API Key">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Search Timeout (seconds):</label>
|
||||||
|
<input type="number" id="soulseek-search-timeout" placeholder="60" min="15" max="300" value="60">
|
||||||
|
<small style="color: #888; display: block; margin-top: 5px;">How long to search for tracks (15-300 seconds)</small>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Search Timeout Buffer (seconds):</label>
|
||||||
|
<input type="number" id="soulseek-search-timeout-buffer" placeholder="15" min="5" max="60" value="15">
|
||||||
|
<small style="color: #888; display: block; margin-top: 5px;">Extra time to wait for late results (5-60 seconds)</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ListenBrainz Settings -->
|
<!-- ListenBrainz Settings -->
|
||||||
|
|
|
||||||
|
|
@ -1629,6 +1629,8 @@ async function loadSettingsData() {
|
||||||
// Populate Soulseek settings
|
// Populate Soulseek settings
|
||||||
document.getElementById('soulseek-url').value = settings.soulseek?.slskd_url || '';
|
document.getElementById('soulseek-url').value = settings.soulseek?.slskd_url || '';
|
||||||
document.getElementById('soulseek-api-key').value = settings.soulseek?.api_key || '';
|
document.getElementById('soulseek-api-key').value = settings.soulseek?.api_key || '';
|
||||||
|
document.getElementById('soulseek-search-timeout').value = settings.soulseek?.search_timeout || 60;
|
||||||
|
document.getElementById('soulseek-search-timeout-buffer').value = settings.soulseek?.search_timeout_buffer || 15;
|
||||||
|
|
||||||
// Populate ListenBrainz settings
|
// Populate ListenBrainz settings
|
||||||
document.getElementById('listenbrainz-token').value = settings.listenbrainz?.token || '';
|
document.getElementById('listenbrainz-token').value = settings.listenbrainz?.token || '';
|
||||||
|
|
@ -2004,7 +2006,9 @@ async function saveSettings() {
|
||||||
slskd_url: document.getElementById('soulseek-url').value,
|
slskd_url: document.getElementById('soulseek-url').value,
|
||||||
api_key: document.getElementById('soulseek-api-key').value,
|
api_key: document.getElementById('soulseek-api-key').value,
|
||||||
download_path: document.getElementById('download-path').value,
|
download_path: document.getElementById('download-path').value,
|
||||||
transfer_path: document.getElementById('transfer-path').value
|
transfer_path: document.getElementById('transfer-path').value,
|
||||||
|
search_timeout: parseInt(document.getElementById('soulseek-search-timeout').value) || 60,
|
||||||
|
search_timeout_buffer: parseInt(document.getElementById('soulseek-search-timeout-buffer').value) || 15
|
||||||
},
|
},
|
||||||
listenbrainz: {
|
listenbrainz: {
|
||||||
token: document.getElementById('listenbrainz-token').value
|
token: document.getElementById('listenbrainz-token').value
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue