Skip to content

Commit dd26f89

Browse files
main Dev sync (#51)
* feat: better bare row name generation * fix: get started button is not functional * fix: get started button is not functional * chore: bump version to 1.0.1 (#38) * feat: Add recency preference and genre diversification to recommendation logic (#44) * feat: Enhance TMDBService with trending and top-rated content retrieval * feat: Add recency preference and genre diversification to recommendation logic * feat: Refactor TMDBService usage to support language preference across services * feat: Refactor library item fetching and caching for improved performance and consistency * feat: Implement caching for language retrieval and refactor auth key encryption * feat: Add middleware for Redis call tracking and enhance token store with call counting * chore: bump version to v1.1.0 * feat: Enhance Redis client management and implement rate limiting for token requests * feat: Refactor catalog fetching and update migration task handling * chore: bump version to v1.1.2 * style: new logo and logo themed ui changes * style: format files * fix: invalidate cache on delete/store (#50)
2 parents 3913092 + cc6d1c6 commit dd26f89

File tree

10 files changed

+135
-94
lines changed

10 files changed

+135
-94
lines changed

app/api/endpoints/catalogs.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,7 @@ async def get_catalog(type: str, id: str, response: Response, token: str):
7676
logger.info(f"Found {len(recommendations)} recommendations for item {item_id}")
7777

7878
elif id.startswith("watchly.theme."):
79-
recommendations = await recommendation_service.get_recommendations_for_theme(
80-
theme_id=id, content_type=type
81-
)
79+
recommendations = await recommendation_service.get_recommendations_for_theme(theme_id=id, content_type=type)
8280
logger.info(f"Found {len(recommendations)} recommendations for theme {id}")
8381

8482
else:

app/api/endpoints/manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def get_config_id(catalog) -> str | None:
8080

8181

8282
async def _manifest_handler(response: Response, token: str):
83-
response.headers["Cache-Control"] = "public, max-age=7200"
83+
response.headers["Cache-Control"] = "no-cache"
8484

8585
if not token:
8686
raise HTTPException(status_code=401, detail="Missing token. Please reconfigure the addon.")

app/core/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.1.2"
1+
__version__ = "1.1.4"

app/services/scoring.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ def process_item(self, raw_item: dict) -> ScoredItem:
3737
source_type="loved" if item.is_loved else ("liked" if item.is_liked else "watched"),
3838
)
3939

40-
def calculate_score(
41-
self, item: dict | StremioLibraryItem, is_loved: bool = False, is_liked: bool = False
42-
) -> float:
40+
def calculate_score(self, item: dict | StremioLibraryItem, is_loved: bool = False, is_liked: bool = False) -> float:
4341
"""
4442
Backwards compatible method to just get the float score.
4543
Accepts either a raw dict or a StremioLibraryItem.

app/services/stremio_service.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,7 @@ async def _post_with_retries(self, client: httpx.AsyncClient, url: str, json: di
422422
except httpx.RequestError as e:
423423
attempts += 1
424424
backoff = (2 ** (attempts - 1)) + random.uniform(0, 0.25)
425-
logger.warning(
426-
f"Stremio POST {url} request error: {e}; retry {attempts}/{max_tries} in {backoff:.2f}s"
427-
)
425+
logger.warning(f"Stremio POST {url} request error: {e}; retry {attempts}/{max_tries} in {backoff:.2f}s")
428426
await asyncio.sleep(backoff)
429427
last_exc = e
430428
continue

app/services/tmdb_service.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,7 @@ async def _make_request(self, endpoint: str, params: dict | None = None) -> dict
6767
try:
6868
return response.json()
6969
except ValueError as e:
70-
logger.error(
71-
f"TMDB API returned invalid JSON for {endpoint}: {e}. Response: {response.text[:200]}"
72-
)
70+
logger.error(f"TMDB API returned invalid JSON for {endpoint}: {e}. Response: {response.text[:200]}")
7371
return {}
7472
except httpx.HTTPStatusError as e:
7573
status = e.response.status_code

app/services/token_store.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def _ensure_secure_salt(self) -> None:
3737
if not settings.TOKEN_SALT or settings.TOKEN_SALT == "change-me":
3838
logger.error("Refusing to store credentials because TOKEN_SALT is unset or using the insecure default.")
3939
raise RuntimeError(
40-
"Server misconfiguration: TOKEN_SALT must be set to a non-default value before storing credentials."
40+
"Server misconfiguration: TOKEN_SALT must be set to a non-default value before storing" " credentials."
4141
)
4242

4343
def _get_cipher(self) -> Fernet:
@@ -145,6 +145,20 @@ async def store_user_data(self, user_id: str, payload: dict[str, Any]) -> str:
145145
else:
146146
await client.set(key, json_str)
147147

148+
# Invalidate async LRU cache for fresh reads on subsequent requests
149+
try:
150+
# bound method supports targeted invalidation by argument(s)
151+
self.get_user_data.cache_invalidate(token)
152+
except KeyError:
153+
# The token was not in the cache, no action needed.
154+
pass
155+
except Exception as e:
156+
logger.warning(f"Targeted cache invalidation failed: {e}. Falling back to clearing cache.")
157+
try:
158+
self.get_user_data.cache_clear()
159+
except Exception as e_clear:
160+
logger.error(f"Error while clearing cache: {e_clear}")
161+
148162
# Ensure we remove from negative cache so new value is read next time
149163
try:
150164
if token in self._missing_tokens:
@@ -194,6 +208,19 @@ async def delete_token(self, token: str = None, key: str = None) -> None:
194208
client = await self._get_client()
195209
await client.delete(key)
196210

211+
# Invalidate async LRU cache so future reads reflect deletion
212+
try:
213+
if token:
214+
self.get_user_data.cache_invalidate(token)
215+
else:
216+
# If only key is provided, clear cache entirely to be safe
217+
self.get_user_data.cache_clear()
218+
except KeyError:
219+
# The token was not in the cache, no action needed.
220+
pass
221+
except Exception as e:
222+
logger.warning(f"Failed to invalidate user data cache during token deletion: {e}")
223+
197224
# Remove from negative cache as token is deleted
198225
try:
199226
if token and token in self._missing_tokens:

0 commit comments

Comments
 (0)