From c493f2aabfd118f4d66fb1ce88d57cb169b5d6ca Mon Sep 17 00:00:00 2001 From: codey Date: Wed, 15 Oct 2025 10:10:56 -0400 Subject: [PATCH] Increase rate limit for lighting state requests and enhance error handling for Cync device operations. Improve lyric search processing by splitting lyrics based on line breaks and cleaning special characters (bugfix for subsearch/seek). --- endpoints/lighting.py | 87 +++++++++++++++++++++++---------------- endpoints/lyric_search.py | 25 ++++++----- 2 files changed, 66 insertions(+), 46 deletions(-) diff --git a/endpoints/lighting.py b/endpoints/lighting.py index 3b60fcf..503f3ee 100644 --- a/endpoints/lighting.py +++ b/endpoints/lighting.py @@ -179,7 +179,7 @@ class Lighting(FastAPI): methods=["GET"], include_in_schema=True, dependencies=[ - Depends(RateLimiter(times=10, seconds=2)), + Depends(RateLimiter(times=25, seconds=2)), Depends(get_current_user), ], ) @@ -190,7 +190,7 @@ class Lighting(FastAPI): methods=["POST"], include_in_schema=True, dependencies=[ - Depends(RateLimiter(times=10, seconds=2)), + Depends(RateLimiter(times=25, seconds=2)), Depends(get_current_user), ], ) @@ -266,7 +266,7 @@ class Lighting(FastAPI): await self.ensure_cync_connection() - # Apply to Cync device + # Validate and extract state values power = state.get("power", "off") if power not in ["on", "off"]: raise HTTPException( @@ -296,41 +296,58 @@ class Lighting(FastAPI): else: rgb = None - # Use persistent Cync API object - if not self.cync_api: - raise HTTPException(status_code=500, detail="Cync API not initialized.") - devices = self.cync_api.get_devices() - if not devices or not isinstance(devices, (list, tuple)): - raise HTTPException( - status_code=500, detail="No devices returned from Cync API." - ) - light = next( - ( - d - for d in devices - if hasattr(d, "name") and d.name == self.cync_device_name - ), - None, - ) - if not light: - raise HTTPException( - status_code=404, - detail=f"Device '{self.cync_device_name}' not found", - ) + # Apply to Cync device with retry on connection issues + max_retries = 2 + for attempt in range(max_retries): + try: + # Use persistent Cync API object + if not self.cync_api: + raise Exception("Cync API not initialized.") + devices = self.cync_api.get_devices() + if not devices or not isinstance(devices, (list, tuple)): + raise Exception("No devices returned from Cync API.") + light = next( + ( + d + for d in devices + if hasattr(d, "name") and d.name == self.cync_device_name + ), + None, + ) + if not light: + raise Exception(f"Device '{self.cync_device_name}' not found") - # Set power - if power == "on": - await light.turn_on() - else: - await light.turn_off() + # Set power + if power == "on": + await light.turn_on() + else: + await light.turn_off() - # Set brightness - if "brightness" in state: - await light.set_brightness(brightness) + # Set brightness + if "brightness" in state: + await light.set_brightness(brightness) - # Set color - if rgb: - await light.set_rgb(rgb) + # Set color + if rgb: + await light.set_rgb(rgb) + + break # Success, exit retry loop + except Exception as e: + if attempt < max_retries - 1: + logging.warning( + "Device operation failed (attempt %d/%d): %s. Retrying with reconnection.", + attempt + 1, + max_retries, + e, + ) + await self.ensure_cync_connection() + else: + logging.error( + "Device operation failed after %d attempts: %s", + max_retries, + e, + ) + raise logging.info( "Successfully applied state to device '%s': %s", diff --git a/endpoints/lyric_search.py b/endpoints/lyric_search.py index c36d2c7..e43ef00 100644 --- a/endpoints/lyric_search.py +++ b/endpoints/lyric_search.py @@ -210,20 +210,20 @@ class LyricSearch(FastAPI): if data.sub and not data.lrc: seeked_found_line: Optional[int] = None - lyric_lines: list[str] = result["lyrics"].strip().split(" / ") + # Split lyrics into lines based on
or newline characters + lyrics_text = result["lyrics"].strip() + if "
" in lyrics_text: + lyric_lines = lyrics_text.split("
") + else: + lyric_lines = lyrics_text.split("\n") for i, line in enumerate(lyric_lines): - line = regex.sub(r"\u2064", "", line.strip()) - if data.sub.strip().lower() in line.strip().lower(): + # Remove any special characters and extra spaces + cleaned_line = regex.sub(r"\u2064", "", line.strip()) + if data.sub.strip().lower() in cleaned_line.lower(): seeked_found_line = i - logging.debug( - "Found %s at %s, match for %s!", - line, - seeked_found_line, - data.sub, - ) # REMOVEME: DEBUG break - if not seeked_found_line: + if seeked_found_line is None: return JSONResponse( status_code=500, content={ @@ -232,7 +232,10 @@ class LyricSearch(FastAPI): "failed_seek": True, }, ) - result["lyrics"] = " / ".join(lyric_lines[seeked_found_line:]) + # Only include lines strictly starting from the matched line + # Use the same separator that was used to split + separator = "
" if "
" in result["lyrics"] else "\n" + result["lyrics"] = separator.join(lyric_lines[seeked_found_line:]) result["confidence"] = int(result["confidence"]) result["time"] = f"{float(result['time']):.4f}"