77from app .core .security import redact_token
88from app .core .settings import UserSettings , get_default_settings
99from app .services .catalog_updater import refresh_catalogs_for_credentials
10- from app .services .recommendation_service import RecommendationService
11- from app .services .stremio_service import StremioService
10+ from app .services .recommendation . engine import RecommendationEngine
11+ from app .services .stremio . service import StremioBundle
1212from app .services .token_store import token_store
1313
1414MAX_RESULTS = 50
@@ -79,27 +79,50 @@ async def get_catalog(type: str, id: str, response: Response, token: str):
7979 credentials = await token_store .get_user_data (token )
8080 if not credentials :
8181 raise HTTPException (status_code = 401 , detail = "Invalid or expired token. Please reconfigure the addon." )
82+
83+ bundle = StremioBundle ()
8284 try :
83- # Extract settings from credentials
85+ # 1. Resolve Auth Key (with potential fallback to login)
86+ auth_key = credentials .get ("authKey" )
87+ email = credentials .get ("email" )
88+ password = credentials .get ("password" )
89+
90+ is_valid = False
91+ if auth_key :
92+ try :
93+ await bundle .auth .get_user_info (auth_key )
94+ is_valid = True
95+ except Exception :
96+ pass
97+
98+ if not is_valid and email and password :
99+ try :
100+ auth_key = await bundle .auth .login (email , password )
101+ credentials ["authKey" ] = auth_key
102+ await token_store .update_user_data (token , credentials )
103+ except Exception as e :
104+ logger .error (f"Failed to refresh auth key during catalog fetch: { e } " )
105+
106+ if not auth_key :
107+ raise HTTPException (status_code = 401 , detail = "Stremio session expired. Please reconfigure." )
108+
109+ # 2. Extract settings from credentials
84110 settings_dict = credentials .get ("settings" , {})
85111 user_settings = UserSettings (** settings_dict ) if settings_dict else get_default_settings ()
86112 language = user_settings .language if user_settings else "en-US"
87113
88- # Create a single service; get_auth_key() will validate/refresh as needed
89- stremio_service = StremioService (
90- username = credentials .get ("email" , "" ),
91- password = credentials .get ("password" , "" ),
92- auth_key = credentials .get ("authKey" ),
93- )
94- # Fetch library once per request and reuse across recommendation paths
95- library_items = await stremio_service .get_library_items ()
96- recommendation_service = RecommendationService (
97- stremio_service = stremio_service ,
114+ # 3. Fetch library once per request and reuse across recommendation paths
115+ library_items = await bundle .library .get_library_items (auth_key )
116+ engine = RecommendationEngine (
117+ stremio_service = bundle ,
98118 language = language ,
99119 user_settings = user_settings ,
100120 token = token ,
101121 library_data = library_items ,
102122 )
123+ # Custom attribute for modularized exclusion logic if needed
124+ # In this refactor, RecommendationFiltering.get_exclusion_sets(stremio_service, library_data)
125+ # is called inside engine, and we pass bundle as stremio_service.
103126
104127 # Resolve per-catalog limits (min/max)
105128 def _get_limits () -> tuple [int , int ]:
@@ -129,13 +152,10 @@ def _get_limits() -> tuple[int, int]:
129152
130153 # Handle item-based recommendations
131154 if id .startswith ("tt" ):
132- try :
133- recommendation_service .per_item_limit = max_items
134- except Exception :
135- pass
136- recommendations = await recommendation_service .get_recommendations_for_item (item_id = id )
155+ engine .per_item_limit = max_items
156+ recommendations = await engine .get_recommendations_for_item (item_id = id , media_type = type )
137157 if len (recommendations ) < min_items :
138- recommendations = await recommendation_service .pad_to_min (type , recommendations , min_items )
158+ recommendations = await engine .pad_to_min (type , recommendations , min_items )
139159 logger .info (f"Found { len (recommendations )} recommendations for { id } " )
140160
141161 elif any (
@@ -148,29 +168,26 @@ def _get_limits() -> tuple[int, int]:
148168 ):
149169 # Extract actual item ID (tt... or tmdb:...)
150170 item_id = re .sub (r"^watchly\.(item|loved|watched)\." , "" , id )
151- try :
152- recommendation_service .per_item_limit = max_items
153- except Exception :
154- pass
155- recommendations = await recommendation_service .get_recommendations_for_item (item_id = item_id )
171+ engine .per_item_limit = max_items
172+ recommendations = await engine .get_recommendations_for_item (item_id = item_id , media_type = type )
156173 if len (recommendations ) < min_items :
157- recommendations = await recommendation_service .pad_to_min (type , recommendations , min_items )
174+ recommendations = await engine .pad_to_min (type , recommendations , min_items )
158175 logger .info (f"Found { len (recommendations )} recommendations for item { item_id } " )
159176
160177 elif id .startswith ("watchly.theme." ):
161- recommendations = await recommendation_service .get_recommendations_for_theme (
178+ recommendations = await engine .get_recommendations_for_theme (
162179 theme_id = id , content_type = type , limit = max_items
163180 )
164181 if len (recommendations ) < min_items :
165- recommendations = await recommendation_service .pad_to_min (type , recommendations , min_items )
182+ recommendations = await engine .pad_to_min (type , recommendations , min_items )
166183 logger .info (f"Found { len (recommendations )} recommendations for theme { id } " )
167184
168185 else :
169- recommendations = await recommendation_service .get_recommendations (
186+ recommendations = await engine .get_recommendations (
170187 content_type = type , source_items_limit = SOURCE_ITEMS_LIMIT , max_results = max_items
171188 )
172189 if len (recommendations ) < min_items :
173- recommendations = await recommendation_service .pad_to_min (type , recommendations , min_items )
190+ recommendations = await engine .pad_to_min (type , recommendations , min_items )
174191 logger .info (f"Found { len (recommendations )} recommendations for { type } " )
175192
176193 logger .info (f"Returning { len (recommendations )} items for { type } " )
@@ -184,6 +201,8 @@ def _get_limits() -> tuple[int, int]:
184201 except Exception as e :
185202 logger .exception (f"[{ redact_token (token )} ] Error fetching catalog for { type } /{ id } : { e } " )
186203 raise HTTPException (status_code = 500 , detail = str (e ))
204+ finally :
205+ await bundle .close ()
187206
188207
189208@router .get ("/{token}/catalog/update" )
0 commit comments