From 1f1abad1377c8cb0c9c6bb7b657916a27bfa12e8 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 12 Feb 2026 01:44:39 +0200 Subject: [PATCH 01/12] game pass improvements --- web/routes/settings.py | 15 +++++++++-- web/services/settings.py | 10 +++++++ web/sources/xbox.py | 53 ++++++++++++++++++++++++++++--------- web/templates/settings.html | 11 ++++++++ 4 files changed, 74 insertions(+), 15 deletions(-) diff --git a/web/routes/settings.py b/web/routes/settings.py index 0637bae..d86b559 100644 --- a/web/routes/settings.py +++ b/web/routes/settings.py @@ -27,7 +27,8 @@ def settings_page( from ..services.settings import ( get_setting, STEAM_ID, STEAM_API_KEY, IGDB_CLIENT_ID, IGDB_CLIENT_SECRET, ITCH_API_KEY, HUMBLE_SESSION_COOKIE, BATTLENET_SESSION_COOKIE, GOG_DB_PATH, - EA_BEARER_TOKEN, IGDB_MATCH_THRESHOLD, LOCAL_GAMES_PATHS + EA_BEARER_TOKEN, IGDB_MATCH_THRESHOLD, LOCAL_GAMES_PATHS, XBOX_XSTS_TOKEN, + XBOX_GAMEPASS_MARKET, XBOX_GAMEPASS_PLAN ) from ..sources.local import discover_local_game_paths @@ -63,6 +64,9 @@ def settings_page( "gog_db_path": get_setting(GOG_DB_PATH, ""), "ea_bearer_token": get_setting(EA_BEARER_TOKEN, ""), "local_games_paths": local_games_paths_value, + "xbox_xsts_token": get_setting(XBOX_XSTS_TOKEN, ""), + "xbox_gamepass_market": get_setting(XBOX_GAMEPASS_MARKET, ""), + "xbox_gamepass_plan": get_setting(XBOX_GAMEPASS_PLAN, ""), } success_flag = success == "1" @@ -97,13 +101,17 @@ def save_settings( gog_db_path: str = Form(default=""), ea_bearer_token: str = Form(default=""), local_games_paths: str = Form(default=""), + xbox_xsts_token: str = Form(default=""), + xbox_gamepass_market: str = Form(default=""), + xbox_gamepass_plan: str = Form(default=""), ): """Save settings from the form.""" # Import here to avoid circular imports from ..services.settings import ( set_setting, STEAM_ID, STEAM_API_KEY, IGDB_CLIENT_ID, IGDB_CLIENT_SECRET, ITCH_API_KEY, HUMBLE_SESSION_COOKIE, BATTLENET_SESSION_COOKIE, GOG_DB_PATH, - EA_BEARER_TOKEN, IGDB_MATCH_THRESHOLD, LOCAL_GAMES_PATHS + EA_BEARER_TOKEN, IGDB_MATCH_THRESHOLD, LOCAL_GAMES_PATHS, XBOX_XSTS_TOKEN, + XBOX_GAMEPASS_MARKET, XBOX_GAMEPASS_PLAN ) # Detect if running in Docker @@ -120,6 +128,9 @@ def save_settings( set_setting(BATTLENET_SESSION_COOKIE, battlenet_session_cookie.strip()) set_setting(GOG_DB_PATH, gog_db_path.strip()) set_setting(EA_BEARER_TOKEN, ea_bearer_token.strip()) + set_setting(XBOX_XSTS_TOKEN, xbox_xsts_token.strip()) + set_setting(XBOX_GAMEPASS_MARKET, xbox_gamepass_market.strip()) + set_setting(XBOX_GAMEPASS_PLAN, xbox_gamepass_plan.strip()) # Only save LOCAL_GAMES_PATHS if not in Docker mode if not is_docker: diff --git a/web/services/settings.py b/web/services/settings.py index 45ec555..45cd6d2 100644 --- a/web/services/settings.py +++ b/web/services/settings.py @@ -19,6 +19,8 @@ GOG_DB_PATH = "gog_db_path" EA_BEARER_TOKEN = "ea_bearer_token" XBOX_XSTS_TOKEN = "xbox_xsts_token" +XBOX_GAMEPASS_MARKET = "xbox_gamepass_market" +XBOX_GAMEPASS_PLAN = "xbox_gamepass_plan" LOCAL_GAMES_PATHS = "local_games_paths" IGDB_MATCH_THRESHOLD = "igdb_match_threshold" @@ -34,6 +36,8 @@ GOG_DB_PATH: "GOG_DB_PATH", EA_BEARER_TOKEN: "EA_BEARER_TOKEN", XBOX_XSTS_TOKEN: "XBOX_XSTS_TOKEN", + XBOX_GAMEPASS_MARKET: "XBOX_GAMEPASS_MARKET", + XBOX_GAMEPASS_PLAN: "XBOX_GAMEPASS_PLAN", LOCAL_GAMES_PATHS: "LOCAL_GAMES_PATHS", IGDB_MATCH_THRESHOLD: "IGDB_MATCH_THRESHOLD", } @@ -179,6 +183,12 @@ def get_xbox_credentials(): "xsts_token": get_setting(XBOX_XSTS_TOKEN), } +def get_xbox_gamepass_settings(): + """Get Xbox Game Pass settings.""" + return { + "market": get_setting(XBOX_GAMEPASS_MARKET), + "plan": get_setting(XBOX_GAMEPASS_PLAN), + } def get_local_games_settings(): """Get local games folder settings.""" diff --git a/web/sources/xbox.py b/web/sources/xbox.py index 12646ad..a109e98 100644 --- a/web/sources/xbox.py +++ b/web/sources/xbox.py @@ -5,19 +5,33 @@ import json import requests -from ..services.settings import get_xbox_credentials +from ..services.settings import get_xbox_credentials, get_xbox_gamepass_settings # Xbox API endpoints TITLEHUB_ENDPOINT = "https://titlehub.xboxlive.com" COLLECTIONS_ENDPOINT = "https://collections.mp.microsoft.com/v9.0/collections/publisherQuery" -GAMEPASS_CATALOG_ENDPOINT = "https://catalog.gamepass.com/sigls/v2" +GAMEPASS_CATALOG_ENDPOINT = "https://catalog.gamepass.com/sigls/v3" DISPLAY_CATALOG_ENDPOINT = "https://displaycatalog.mp.microsoft.com/v7.0/products" -# Game Pass catalog IDs -# fdd9e2a7-0fee-49f6-ad69-4354098401ff = PC Game Pass -# f6f1f99f-9b49-4ccd-b3bf-4d9767a77f5e = Console Game Pass -# 29a81209-df6f-41fd-a528-2ae6b91f719c = EA Play -GAMEPASS_PC_CATALOG_ID = "fdd9e2a7-0fee-49f6-ad69-4354098401ff" +GAMEPASS_PLAN_MAP = { + "ultimate": + { + "collection": "97c6c862-d28a-4907-a3d5-c401f2296a53", + "subscription": "cfq7ttc0khs0" + }, + "premium": { + "collection": "09a72c0d-c466-426a-9580-b78955d8173a", + "subscription": "cfq7ttc0p85b" + }, + "essential": { + "collection": "34031711-5a70-4196-bab7-45757dc2294e", + "subscription": "cfq7ttc0k5dj" + }, + "pc": { + "collection": "609d944c-d395-4c0a-9ea4-e9f39b52c1ad", + "subscription": "cfq7ttc0kgq8" + } +} # Required headers for API requests REQUIRED_HEADERS = { @@ -27,6 +41,14 @@ "x-xbl-contract-version": "2", } +def get_resolved_xbox_gamepass_settings(): + """Get Xbox Game Pass settings.""" + gamepass_settings = get_xbox_gamepass_settings() + return { + "plan": gamepass_settings.get("plan", "ultimate"), + "market": gamepass_settings.get("market", "US") + } + def get_xsts_token(): """Get stored XSTS token from settings.""" @@ -307,16 +329,18 @@ def get_owned_games_from_collections(token): print(f" Error fetching from Collections API: {e}") return [] - -def get_gamepass_catalog(): +def get_gamepass_catalog(plan, market): """Fetch Game Pass PC catalog (public API, no auth required).""" try: all_games = [] + planInfo = GAMEPASS_PLAN_MAP[plan] + collectionId = planInfo['collection'] + subscriptionId = planInfo['subscription'] # Fetch Game Pass catalog - url = f"{GAMEPASS_CATALOG_ENDPOINT}?id={GAMEPASS_PC_CATALOG_ID}&language=en-US&market=US" + url = f"{GAMEPASS_CATALOG_ENDPOINT}?id={collectionId}&language=en-US&market={market}&platformContext=pc&subscriptionContext={subscriptionId}" - print(" Fetching Game Pass catalog...") + print(f" Fetching Game Pass catalog for plan: {plan} (market: {market})...") response = requests.get(url, headers=REQUIRED_HEADERS) if response.status_code != 200: @@ -430,6 +454,7 @@ def get_product_details(product_ids): def get_xbox_library(): """Fetch all games from Xbox - owned games + Game Pass catalog.""" token = get_xsts_token() + gamepass_settings = get_resolved_xbox_gamepass_settings() print("Fetching Xbox library...") @@ -451,7 +476,7 @@ def get_xbox_library(): print(" To import owned games, add your XSTS token in Settings") # Then fetch Game Pass catalog (public API) - gamepass_games = get_gamepass_catalog() + gamepass_games = get_gamepass_catalog(gamepass_settings["plan"], gamepass_settings["market"]) print(f" Found {len(gamepass_games)} Game Pass games") # Add Game Pass games that aren't already owned @@ -488,6 +513,8 @@ def main(): parser.add_argument("--token", type=str, help="XSTS token (for testing)") parser.add_argument("--gamepass-only", action="store_true", help="Only fetch Game Pass catalog") parser.add_argument("--export", type=str, help="Export to JSON file instead of database") + parser.add_argument("--plan", type=str, choices=["ultimate", "premium", "essential", "pc"], default="ultimate", help="Game Pass plan to fetch (default: ultimate)") + parser.add_argument("--market", type=str, default="US", help="Game Pass market to fetch (default: US)") args = parser.parse_args() print("Xbox Library Import") @@ -495,7 +522,7 @@ def main(): if args.gamepass_only: print("Fetching Game Pass catalog only...") - games = get_gamepass_catalog() + games = get_gamepass_catalog(args.plan, args.market) elif args.token: # Use provided token for testing print("Using provided token...") diff --git a/web/templates/settings.html b/web/templates/settings.html index 66519d1..9ece589 100644 --- a/web/templates/settings.html +++ b/web/templates/settings.html @@ -1529,6 +1529,17 @@

Option 2: B Game Pass catalog is public and will sync without a token. Token is only needed for your owned games.

+ + + + From 37ea28e2a2341c70431ec3ab065c279d3ec6ff8d Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 12 Feb 2026 01:54:45 +0200 Subject: [PATCH 02/12] select element style --- web/templates/settings.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/web/templates/settings.html b/web/templates/settings.html index 9ece589..10d69b9 100644 --- a/web/templates/settings.html +++ b/web/templates/settings.html @@ -334,6 +334,22 @@ box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.3); } + .form-group select { + width: 100%; + padding: 12px 16px; + border: none; + border-radius: 8px; + background: rgba(255, 255, 255, 0.1); + color: #e4e4e4; + font-size: 1rem; + } + + .form-group select:focus { + outline: none; + background: rgba(255, 255, 255, 0.15); + box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.3); + } + .help-text { font-size: 0.8rem; color: #888; From 67c67b63776e36891395c219812db1a1e57aeb10 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 12 Feb 2026 01:55:50 +0200 Subject: [PATCH 03/12] select background --- web/templates/settings.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/templates/settings.html b/web/templates/settings.html index 10d69b9..e12002d 100644 --- a/web/templates/settings.html +++ b/web/templates/settings.html @@ -350,6 +350,11 @@ box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.3); } + .form-group select option { + background: #1a1a2e; + color: #e4e4e4; + } + .help-text { font-size: 0.8rem; color: #888; From 18dc7b32ec24d28109c03181c3d2bfcb2c3f68bf Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 12 Feb 2026 02:01:04 +0200 Subject: [PATCH 04/12] fix sort select styling in main library view --- web/templates/index.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/templates/index.html b/web/templates/index.html index 61c0675..3ccf921 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -545,6 +545,16 @@ cursor: pointer; } + .sort-select option { + background: #1a1a2e; + color: #e4e4e4; + } + + .sort-select option:checked { + background: #1a1a2e; + color: #e4e4e4; + } + .games-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); From 3f39623c08b3dfcd8c69665fecf891dabdcec323 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 12 Feb 2026 02:23:09 +0200 Subject: [PATCH 05/12] use plan info instead of hardcoded markets also in product details and owned games --- web/sources/xbox.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/sources/xbox.py b/web/sources/xbox.py index a109e98..6e98e84 100644 --- a/web/sources/xbox.py +++ b/web/sources/xbox.py @@ -274,12 +274,13 @@ def get_owned_games_from_collections(token): } # Query for owned products + planInfo = get_resolved_xbox_gamepass_settings() payload = { "productIds": [], "productSkuIds": [], "idType": "ProductId", "beneficiaries": [], - "market": "US", + "market": planInfo["market"], "languages": ["en-US"], "maxPageSize": 1000, } @@ -383,7 +384,8 @@ def get_product_details(product_ids): try: # Build the products query ids_param = ",".join(product_ids) - url = f"{DISPLAY_CATALOG_ENDPOINT}?bigIds={ids_param}&market=US&languages=en-US" + planInfo = get_resolved_xbox_gamepass_settings() + url = f"{DISPLAY_CATALOG_ENDPOINT}?bigIds={ids_param}&market={planInfo['market']}&languages=en-US" response = requests.get(url, headers=REQUIRED_HEADERS) From acf4dcefa49858bdfd1cc79c8248df3a6c20c974 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 12 Feb 2026 11:05:54 +0200 Subject: [PATCH 06/12] game pass - allow no plan (`none`) --- web/services/database_builder.py | 2 +- web/sources/xbox.py | 8 +++++++- web/templates/settings.html | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/web/services/database_builder.py b/web/services/database_builder.py index d791fd5..12331d3 100644 --- a/web/services/database_builder.py +++ b/web/services/database_builder.py @@ -621,7 +621,7 @@ def import_xbox_games(conn): if not games: print(" No Xbox games found or not configured") print(" Add your XSTS token in Settings to import owned games") - print(" Game Pass catalog will be imported regardless") + print(" Select Game Pass plan in Settings to import catalog") return 0 count = 0 diff --git a/web/sources/xbox.py b/web/sources/xbox.py index 6e98e84..35eecfa 100644 --- a/web/sources/xbox.py +++ b/web/sources/xbox.py @@ -8,6 +8,8 @@ from ..services.settings import get_xbox_credentials, get_xbox_gamepass_settings # Xbox API endpoints +# Reference: https://www.xbox.com/en-us/xbox-game-pass/games/js/xgpcatPopulate-2025.js + TITLEHUB_ENDPOINT = "https://titlehub.xboxlive.com" COLLECTIONS_ENDPOINT = "https://collections.mp.microsoft.com/v9.0/collections/publisherQuery" GAMEPASS_CATALOG_ENDPOINT = "https://catalog.gamepass.com/sigls/v3" @@ -333,8 +335,12 @@ def get_owned_games_from_collections(token): def get_gamepass_catalog(plan, market): """Fetch Game Pass PC catalog (public API, no auth required).""" try: + if plan == "none": + print(" Game Pass import disabled - skipping") + return [] + all_games = [] - + planInfo = GAMEPASS_PLAN_MAP[plan] collectionId = planInfo['collection'] subscriptionId = planInfo['subscription'] diff --git a/web/templates/settings.html b/web/templates/settings.html index e12002d..017aea2 100644 --- a/web/templates/settings.html +++ b/web/templates/settings.html @@ -1556,10 +1556,11 @@

Option 2: B placeholder="e.g. US, AU"> From 09ba552328aa01b5dc434f94eda2e0db979d7db1 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 13 Feb 2026 11:34:10 +0200 Subject: [PATCH 07/12] fix IGDB ID --- web/templates/game_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/templates/game_detail.html b/web/templates/game_detail.html index 030e656..d109e70 100644 --- a/web/templates/game_detail.html +++ b/web/templates/game_detail.html @@ -1544,7 +1544,7 @@

IGDB Data

Find the IGDB ID by searching on igdb.com. - The ID is in the URL (e.g., igdb.com/games/slug/123 → ID is 123). + The ID is displayed in a box to the right of the metadata pane (e.g. IGDB ID: 12345).

From 1bf29180e2e3cf028c704c44de4ac2c262205d81 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 13 Feb 2026 13:03:47 +0200 Subject: [PATCH 08/12] fix xbox store URL --- web/utils/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/utils/helpers.py b/web/utils/helpers.py index 75e84cd..aca9b2d 100644 --- a/web/utils/helpers.py +++ b/web/utils/helpers.py @@ -56,7 +56,7 @@ def get_store_url(store, store_id, extra_data=None): elif store == "xbox": # Xbox Store URL if store_id: - return f"https://www.xbox.com/games/store/{store_id}" + return f"https://www.xbox.com/games/store/game/{store_id}" return None return None From de9dcfd49f7c674fce08a9e20287d6c14af85dd5 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 13 Feb 2026 22:52:47 +0200 Subject: [PATCH 09/12] default game pass plan should be `none` --- web/sources/xbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/sources/xbox.py b/web/sources/xbox.py index 35eecfa..f161c3f 100644 --- a/web/sources/xbox.py +++ b/web/sources/xbox.py @@ -47,7 +47,7 @@ def get_resolved_xbox_gamepass_settings(): """Get Xbox Game Pass settings.""" gamepass_settings = get_xbox_gamepass_settings() return { - "plan": gamepass_settings.get("plan", "ultimate"), + "plan": gamepass_settings.get("plan", "none"), "market": gamepass_settings.get("market", "US") } From 950fb72061b5590add7ad294518d3e3f9df3d594 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 13 Feb 2026 22:53:50 +0200 Subject: [PATCH 10/12] fix var naming conventions --- web/sources/xbox.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/web/sources/xbox.py b/web/sources/xbox.py index f161c3f..bc0b2f5 100644 --- a/web/sources/xbox.py +++ b/web/sources/xbox.py @@ -341,11 +341,15 @@ def get_gamepass_catalog(plan, market): all_games = [] - planInfo = GAMEPASS_PLAN_MAP[plan] - collectionId = planInfo['collection'] - subscriptionId = planInfo['subscription'] + plan_info = GAMEPASS_PLAN_MAP.get(plan) + if not plan_info: + print(f" Invalid Game Pass plan: {plan}") + return [] + + collection_id = plan_info['collection'] + subscription_id = plan_info['subscription'] # Fetch Game Pass catalog - url = f"{GAMEPASS_CATALOG_ENDPOINT}?id={collectionId}&language=en-US&market={market}&platformContext=pc&subscriptionContext={subscriptionId}" + url = f"{GAMEPASS_CATALOG_ENDPOINT}?id={collection_id}&language=en-US&market={market}&platformContext=pc&subscriptionContext={subscription_id}" print(f" Fetching Game Pass catalog for plan: {plan} (market: {market})...") response = requests.get(url, headers=REQUIRED_HEADERS) From 5752b38c19ddea6b77589cde8aec0e327fc1d44e Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 13 Feb 2026 23:02:05 +0200 Subject: [PATCH 11/12] propagate config to inner methods via params --- web/sources/xbox.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/web/sources/xbox.py b/web/sources/xbox.py index bc0b2f5..c7585a7 100644 --- a/web/sources/xbox.py +++ b/web/sources/xbox.py @@ -164,7 +164,7 @@ def get_xuid_from_token(token): return None -def get_owned_games(token, xuid=None): +def get_owned_games(token, market, xuid=None): """Fetch owned games from Xbox TitleHub API.""" try: auth_header, userhash = parse_xsts_token(token) @@ -179,7 +179,7 @@ def get_owned_games(token, xuid=None): if not xuid: print(" Could not determine XUID - trying alternative API") # Fall back to Collections API which doesn't need XUID - return get_owned_games_from_collections(token) + return get_owned_games_from_collections(token, market) headers = { **REQUIRED_HEADERS, @@ -204,7 +204,7 @@ def get_owned_games(token, xuid=None): if response.status_code != 200: print(f" TitleHub error: {response.status_code} - {response.text[:200]}") # Try Collections API as fallback - return get_owned_games_from_collections(token) + return get_owned_games_from_collections(token, market) try: data = response.json() @@ -262,7 +262,7 @@ def get_owned_games(token, xuid=None): return [] -def get_owned_games_from_collections(token): +def get_owned_games_from_collections(token, market): """Fetch owned games using Collections API (alternative method).""" try: auth_header, _ = parse_xsts_token(token) @@ -276,13 +276,12 @@ def get_owned_games_from_collections(token): } # Query for owned products - planInfo = get_resolved_xbox_gamepass_settings() payload = { "productIds": [], "productSkuIds": [], "idType": "ProductId", "beneficiaries": [], - "market": planInfo["market"], + "market": market, "languages": ["en-US"], "maxPageSize": 1000, } @@ -341,11 +340,7 @@ def get_gamepass_catalog(plan, market): all_games = [] - plan_info = GAMEPASS_PLAN_MAP.get(plan) - if not plan_info: - print(f" Invalid Game Pass plan: {plan}") - return [] - + plan_info = GAMEPASS_PLAN_MAP[plan] collection_id = plan_info['collection'] subscription_id = plan_info['subscription'] # Fetch Game Pass catalog @@ -374,7 +369,7 @@ def get_gamepass_catalog(plan, market): batch_size = 20 for i in range(0, len(product_ids), batch_size): batch = product_ids[i:i + batch_size] - details = get_product_details(batch) + details = get_product_details(batch, market) all_games.extend(details) return all_games @@ -386,7 +381,7 @@ def get_gamepass_catalog(plan, market): return [] -def get_product_details(product_ids): +def get_product_details(product_ids, market): """Fetch product details from Display Catalog API.""" if not product_ids: return [] @@ -394,8 +389,7 @@ def get_product_details(product_ids): try: # Build the products query ids_param = ",".join(product_ids) - planInfo = get_resolved_xbox_gamepass_settings() - url = f"{DISPLAY_CATALOG_ENDPOINT}?bigIds={ids_param}&market={planInfo['market']}&languages=en-US" + url = f"{DISPLAY_CATALOG_ENDPOINT}?bigIds={ids_param}&market={market}&languages=en-US" response = requests.get(url, headers=REQUIRED_HEADERS) @@ -467,7 +461,8 @@ def get_xbox_library(): """Fetch all games from Xbox - owned games + Game Pass catalog.""" token = get_xsts_token() gamepass_settings = get_resolved_xbox_gamepass_settings() - + plan = gamepass_settings["plan"] + market = gamepass_settings["market"] print("Fetching Xbox library...") all_games = [] @@ -475,7 +470,7 @@ def get_xbox_library(): # First, try to get owned games if token is available if token: - owned_games = get_owned_games(token) + owned_games = get_owned_games(token, market) print(f" Found {len(owned_games)} owned Xbox games") for game in owned_games: @@ -488,7 +483,7 @@ def get_xbox_library(): print(" To import owned games, add your XSTS token in Settings") # Then fetch Game Pass catalog (public API) - gamepass_games = get_gamepass_catalog(gamepass_settings["plan"], gamepass_settings["market"]) + gamepass_games = get_gamepass_catalog(plan, market) print(f" Found {len(gamepass_games)} Game Pass games") # Add Game Pass games that aren't already owned @@ -538,7 +533,7 @@ def main(): elif args.token: # Use provided token for testing print("Using provided token...") - games = get_owned_games(args.token) + games = get_owned_games(args.token, args.market) else: games = get_xbox_library() From 85e8a5a6f5d22642a47aeb2b6ae675aa4059bb68 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 13 Feb 2026 23:06:01 +0200 Subject: [PATCH 12/12] raise error on invalid game pass plan --- web/sources/xbox.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/sources/xbox.py b/web/sources/xbox.py index c7585a7..cfdfdc0 100644 --- a/web/sources/xbox.py +++ b/web/sources/xbox.py @@ -340,7 +340,10 @@ def get_gamepass_catalog(plan, market): all_games = [] - plan_info = GAMEPASS_PLAN_MAP[plan] + plan_info = GAMEPASS_PLAN_MAP.get(plan) + if not plan_info: + raise ValueError(f"Invalid Game Pass plan: {plan}") + collection_id = plan_info['collection'] subscription_id = plan_info['subscription'] # Fetch Game Pass catalog