# SPDX-License-Identifier: AGPL-3.0-or-later """SensCritique (movies) """ from __future__ import annotations from json import dumps, loads from typing import Any, Optional from searx.result_types import EngineResults, MainResult about = { "website": 'https://www.senscritique.com/', "wikidata_id": 'Q16676060', "official_api_documentation": None, "use_official_api": False, "require_api_key": False, "results": 'JSON', 'language': 'fr', } categories = ['movies'] paging = True page_size = 16 graphql_url = 'https://apollo.senscritique.com/' graphql_query = """query SearchProductExplorer($query: String, $offset: Int, $limit: Int, $sortBy: SearchProductExplorerSort) { searchProductExplorer( query: $query filters: [] sortBy: $sortBy offset: $offset limit: $limit ) { items { category dateRelease duration id originalTitle rating title url yearOfProduction medias { picture } countries { name } genresInfos { label } directors { name } stats { ratingCount } } } }""" def request(query: str, params: dict[str, Any]) -> dict[str, Any]: offset = (params['pageno'] - 1) * page_size data = { "operationName": "SearchProductExplorer", "variables": {"offset": offset, "limit": page_size, "query": query, "sortBy": "RELEVANCE"}, "query": graphql_query, } params['url'] = graphql_url params['method'] = 'POST' params['headers']['Content-Type'] = 'application/json' params['data'] = dumps(data) return params def response(resp) -> EngineResults: res = EngineResults() response_data = loads(resp.text) items = response_data.get('data', {}).get('searchProductExplorer', {}).get('items', []) if not items: return res for item in items: result = parse_item(item) if not result: continue res.add(result=result) return res def parse_item(item: dict[str, Any]) -> MainResult | None: """Parse a single item from the SensCritique API response""" title = item.get('title', '') if not title: return None year = item.get('yearOfProduction') original_title = item.get('originalTitle') thumbnail: str = "" if item.get('medias', {}) and item['medias'].get('picture'): thumbnail = item['medias']['picture'] content_parts = build_content_parts(item, title, original_title) url = f"https://www.senscritique.com{item['url']}" return MainResult( url=url, title=title + (f' ({year})' if year else ''), content=' | '.join(content_parts), thumbnail=thumbnail, ) def build_content_parts(item: dict[str, Any], title: str, original_title: Optional[str]) -> list[str]: """Build the content parts for an item""" content_parts = [] if item.get('category'): content_parts.append(item['category']) if original_title and original_title != title: content_parts.append(f"Original title: {original_title}") if item.get('directors'): directors = [director['name'] for director in item['directors']] content_parts.append(f"Director(s): {', '.join(directors)}") if item.get('countries'): countries = [country['name'] for country in item['countries']] content_parts.append(f"Country: {', '.join(countries)}") if item.get('genresInfos'): genres = [genre['label'] for genre in item['genresInfos']] content_parts.append(f"Genre(s): {', '.join(genres)}") if item.get('duration'): minutes = item['duration'] // 60 if minutes > 0: content_parts.append(f"Duration: {minutes} min") if item.get('rating') and item.get('stats', {}).get('ratingCount'): content_parts.append(f"Rating: {item['rating']}/10 ({item['stats']['ratingCount']} votes)") return content_parts