summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobinFrcd <29704178+RobinFrcd@users.noreply.github.com>2025-04-12 00:24:25 +0200
committerMarkus Heiser <markus.heiser@darmarIT.de>2025-04-17 10:19:22 +0200
commit087da66565dfea0f020172f47939e00fb1691def (patch)
tree5fc20504b132b6ff95feef3cde16041228342a49
parentb84ae39978672c85b866ec48804d749c60c13e63 (diff)
[feat] add SensCritique (FR) engine
Closes: https://github.com/searxng/searxng/issues/4623
-rw-r--r--searx/engines/senscritique.py151
-rw-r--r--searx/settings.yml6
2 files changed, 157 insertions, 0 deletions
diff --git a/searx/engines/senscritique.py b/searx/engines/senscritique.py
new file mode 100644
index 000000000..3abd7d358
--- /dev/null
+++ b/searx/engines/senscritique.py
@@ -0,0 +1,151 @@
+# 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
diff --git a/searx/settings.yml b/searx/settings.yml
index ec72f8736..435862723 100644
--- a/searx/settings.yml
+++ b/searx/settings.yml
@@ -2642,6 +2642,12 @@ engines:
shortcut: pgo
disabled: true
+ - name: senscritique
+ engine: senscritique
+ shortcut: scr
+ timeout: 4.0
+ disabled: true
+
# Doku engine lets you access to any Doku wiki instance:
# A public one or a privete/corporate one.
# - name: ubuntuwiki