Compare commits
No commits in common. "6885482aea49345c096ec5ab381d1f4093420a71" and "5bfd23995c49eb67dbb9a4fed83daf69606d8880" have entirely different histories.
6885482aea
...
5bfd23995c
@ -42,7 +42,7 @@
|
|||||||
- [ ] Добавить запуск и скачивание игр с EGS
|
- [ ] Добавить запуск и скачивание игр с EGS
|
||||||
- [ ] Добавить авторизацию в EGS через WebView, а не вручную
|
- [ ] Добавить авторизацию в EGS через WebView, а не вручную
|
||||||
- [X] Брать описания для игр с EGS из их [api](https://store-content.ak.epicgames.com/api)
|
- [X] Брать описания для игр с EGS из их [api](https://store-content.ak.epicgames.com/api)
|
||||||
- [X] Брать slug через Graphql [запрос](https://launcher.store.epicgames.com/graphql)
|
- [ ] Брать slug через Graphql [запрос](https://launcher.store.epicgames.com/graphql)
|
||||||
- [X] Добавить на карточку бейдж того что игра со стима
|
- [X] Добавить на карточку бейдж того что игра со стима
|
||||||
- [X] Добавить поддержку Flatpak и Snap версии Steam
|
- [X] Добавить поддержку Flatpak и Snap версии Steam
|
||||||
- [X] Выводить данные о самом недавнем пользователе Steam, а не первом попавшемся
|
- [X] Выводить данные о самом недавнем пользователе Steam, а не первом попавшемся
|
||||||
@ -58,7 +58,6 @@
|
|||||||
- [X] Исправить частичное применение тем на лету
|
- [X] Исправить частичное применение тем на лету
|
||||||
- [X] Исправить наложение подписей скриншотов при первом перелистывание в полноэкранном режиме
|
- [X] Исправить наложение подписей скриншотов при первом перелистывание в полноэкранном режиме
|
||||||
- [ ] Добавить GOG (?)
|
- [ ] Добавить GOG (?)
|
||||||
- [ ] Определится уже наконец с названием (PortProtonQt или PortProtonQT)
|
|
||||||
|
|
||||||
### Установка (debug)
|
### Установка (debug)
|
||||||
|
|
||||||
|
@ -30,12 +30,10 @@ def get_egs_game_description_async(
|
|||||||
cache_ttl: int = 3600
|
cache_ttl: int = 3600
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Asynchronously fetches the game description using Epic Games Store GraphQL API.
|
Asynchronously fetches the game description from the Epic Games Store API.
|
||||||
Falls back to the legacy store-content API using the productSlug from GraphQL if available.
|
|
||||||
Uses per-app cache files named egs_app_{app_name}.json in ~/.cache/PortProtonQT.
|
Uses per-app cache files named egs_app_{app_name}.json in ~/.cache/PortProtonQT.
|
||||||
Checks the cache first; if the description is cached and not expired, returns it.
|
Checks the cache first; if the description is cached and not expired, returns it.
|
||||||
Uses system language from get_egs_language() for the description.
|
Prioritizes the page with type 'productHome' for the base game description.
|
||||||
Prioritizes the main game description by filtering for productSlug and excluding DLC/bundles.
|
|
||||||
"""
|
"""
|
||||||
cache_dir = get_cache_dir()
|
cache_dir = get_cache_dir()
|
||||||
cache_file = cache_dir / f"egs_app_{app_name.lower().replace(':', '_').replace(' ', '_')}.json"
|
cache_file = cache_dir / f"egs_app_{app_name.lower().replace(':', '_').replace(' ', '_')}.json"
|
||||||
@ -86,67 +84,39 @@ def get_egs_game_description_async(
|
|||||||
cache_file.unlink(missing_ok=True)
|
cache_file.unlink(missing_ok=True)
|
||||||
|
|
||||||
lang = get_egs_language()
|
lang = get_egs_language()
|
||||||
search_url = "https://graphql.epicgames.com/graphql"
|
slug = app_name.lower().replace(":", "").replace(" ", "-")
|
||||||
headers = {
|
url = f"https://store-content.ak.epicgames.com/api/{lang}/content/products/{slug}"
|
||||||
"Content-Type": "application/json",
|
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) EpicGamesLauncher"
|
|
||||||
}
|
|
||||||
search_query = {
|
|
||||||
"query": "query search($keywords: String!, $locale: String) { Catalog { searchStore(keywords: $keywords, locale: $locale) { elements { title namespace productSlug description } } } }",
|
|
||||||
"variables": {
|
|
||||||
"keywords": app_name,
|
|
||||||
"locale": lang
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def fetch_description():
|
def fetch_description():
|
||||||
description = ""
|
|
||||||
product_slug = None
|
|
||||||
try:
|
try:
|
||||||
# First attempt: GraphQL search query
|
response = requests.get(url, timeout=5)
|
||||||
response = requests.post(search_url, json=search_query, headers=headers, timeout=5)
|
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = orjson.loads(response.content)
|
data = orjson.loads(response.content)
|
||||||
|
|
||||||
if isinstance(data, dict) and "data" in data:
|
if not isinstance(data, dict):
|
||||||
elements = data.get("data", {}).get("Catalog", {}).get("searchStore", {}).get("elements", [])
|
logger.warning("Invalid JSON structure for %s: %s", app_name, type(data))
|
||||||
for element in elements:
|
callback("")
|
||||||
if isinstance(element, dict) and element.get("title", "").lower() == app_name.lower() and element.get("productSlug") and not any(substring in element.get("title", "").lower() for substring in ["bundle", "pack", "edition", "dlc", "upgrade", "chapter", "набор", "пак", "дополнение"]):
|
return
|
||||||
description = element.get("description", "")
|
|
||||||
product_slug = element.get("productSlug", "")
|
description = ""
|
||||||
|
pages = data.get("pages", [])
|
||||||
|
if pages:
|
||||||
|
# Look for the page with type "productHome" for the base game
|
||||||
|
for page in pages:
|
||||||
|
if page.get("type") == "productHome":
|
||||||
|
about_data = page.get("data", {}).get("about", {})
|
||||||
|
description = about_data.get("shortDescription", "")
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
logger.warning("Invalid JSON structure for %s in GraphQL response: %s", app_name, type(data))
|
# Fallback to first page's description if no productHome is found
|
||||||
|
description = (
|
||||||
if not description and product_slug:
|
pages[0].get("data", {})
|
||||||
logger.info("No valid description found in GraphQL for %s, falling back to legacy API with slug %s", app_name, product_slug)
|
.get("about", {})
|
||||||
# Fallback to legacy API using productSlug
|
.get("shortDescription", "")
|
||||||
legacy_url = f"https://store-content.ak.epicgames.com/api/{lang}/content/products/{product_slug}"
|
)
|
||||||
response = requests.get(legacy_url, timeout=5)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = orjson.loads(response.content)
|
|
||||||
|
|
||||||
if not isinstance(data, dict):
|
|
||||||
logger.warning("Invalid JSON structure for %s in legacy API: %s", app_name, type(data))
|
|
||||||
callback("")
|
|
||||||
return
|
|
||||||
|
|
||||||
pages = data.get("pages", [])
|
|
||||||
if pages:
|
|
||||||
for page in pages:
|
|
||||||
if page.get("type") == "productHome":
|
|
||||||
about_data = page.get("data", {}).get("about", {})
|
|
||||||
description = about_data.get("shortDescription", "")
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
description = (
|
|
||||||
pages[0].get("data", {})
|
|
||||||
.get("about", {})
|
|
||||||
.get("shortDescription", "")
|
|
||||||
)
|
|
||||||
|
|
||||||
if not description:
|
if not description:
|
||||||
logger.warning("No valid description found for %s after both queries", app_name)
|
logger.warning("No valid description found for %s", app_name)
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Fetched EGS description for %s: %s",
|
"Fetched EGS description for %s: %s",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user