From 315cdeef41563fbdc59d64f5a5450773cd922e82 Mon Sep 17 00:00:00 2001 From: Vlad Jerca Date: Wed, 11 Feb 2026 15:32:07 +0100 Subject: [PATCH 1/3] refactor: inject Trakt API keys using obfuscation Injects Trakt client ID and secret into the traktapi.py file during the build process. --- .github/workflows/submit.yml | 5 ++++ resources/lib/obfuscation.py | 11 +++++++++ resources/lib/traktapi.py | 9 ++++--- scripts/inject_keys.py | 47 ++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 resources/lib/obfuscation.py create mode 100644 scripts/inject_keys.py diff --git a/.github/workflows/submit.yml b/.github/workflows/submit.yml index 799bc0e6..c1784fe8 100644 --- a/.github/workflows/submit.yml +++ b/.github/workflows/submit.yml @@ -13,6 +13,11 @@ jobs: steps: - name: Checkout uses: actions/checkout@v5 + - name: Inject Trakt Keys + env: + TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }} + TRAKT_CLIENT_SECRET: ${{ secrets.TRAKT_CLIENT_SECRET }} + run: python3 scripts/inject_keys.py - name: Generate distribution zip and submit to official kodi repository id: kodi-addon-submitter uses: xbmc/action-kodi-addon-submitter@v1.3 diff --git a/resources/lib/obfuscation.py b/resources/lib/obfuscation.py new file mode 100644 index 00000000..99b54eaa --- /dev/null +++ b/resources/lib/obfuscation.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +def deobfuscate(data): + if not data or not isinstance(data, list): + return "" + return "".join(chr(b ^ 0x42) for b in data) + +def obfuscate(data): + if not data: + return [] + return [ord(c) ^ 0x42 for c in data] diff --git a/resources/lib/traktapi.py b/resources/lib/traktapi.py index 30b265ec..116e0a5d 100644 --- a/resources/lib/traktapi.py +++ b/resources/lib/traktapi.py @@ -20,6 +20,7 @@ findSeasonMatchInList, findShowMatchInList, ) +from resources.lib.obfuscation import deobfuscate from trakt import Trakt from trakt.objects import Movie, Show @@ -31,8 +32,9 @@ class traktAPI(object): - __client_id = "d4161a7a106424551add171e5470112e4afdaf2438e6ef2fe0548edc75924868" - __client_secret = "b5fcd7cb5d9bb963784d11bbf8535bc0d25d46225016191eb48e50792d2155c0" + # Placeholders for build-time injection + __client_id = "TRAKT_CLIENT_ID_PLACEHOLDER" + __client_secret = "TRAKT_CLIENT_SECRET_PLACEHOLDER" def __init__(self, force=False): logger.debug("Initializing.") @@ -43,7 +45,8 @@ def __init__(self, force=False): # Configure Trakt.configuration.defaults.client( - id=self.__client_id, secret=self.__client_secret + id=deobfuscate(self.__client_id), + secret=deobfuscate(self.__client_secret), ) # Bind event diff --git a/scripts/inject_keys.py b/scripts/inject_keys.py new file mode 100644 index 00000000..59cd874b --- /dev/null +++ b/scripts/inject_keys.py @@ -0,0 +1,47 @@ +import os +import sys + +# Add the parent directory to sys.path to allow importing from resources +sys.path.append(os.path.join(os.path.dirname(__file__), "..")) + +from resources.lib.obfuscation import deobfuscate, obfuscate + +def main(): + client_id = os.environ.get("TRAKT_CLIENT_ID") + client_secret = os.environ.get("TRAKT_CLIENT_SECRET") + + if not client_id or not client_secret: + print("Error: TRAKT_CLIENT_ID or TRAKT_CLIENT_SECRET not set.") + sys.exit(1) + + obfuscated_id = obfuscate(client_id) + obfuscated_secret = obfuscate(client_secret) + + # Verify logic + assert deobfuscate(obfuscated_id) == client_id + assert deobfuscate(obfuscated_secret) == client_secret + + target_file = "resources/lib/traktapi.py" + with open(target_file, "r") as f: + content = f.read() + + # Replace placeholders + new_content = content.replace( + '"TRAKT_CLIENT_ID_PLACEHOLDER"', + str(obfuscated_id), + ).replace( + '"TRAKT_CLIENT_SECRET_PLACEHOLDER"', + str(obfuscated_secret), + ) + + if new_content == content: + print("Error: Could not find placeholders in target file.") + sys.exit(1) + + with open(target_file, "w") as f: + f.write(new_content) + + print(f"Successfully injected obfuscated keys into {target_file}") + +if __name__ == "__main__": + main() From 309e54d3d00318b49ebce9ec098a4c5fdc2d0839 Mon Sep 17 00:00:00 2001 From: Vlad Jerca Date: Wed, 11 Feb 2026 15:47:24 +0100 Subject: [PATCH 2/3] chore: enable local dev via env variables Allows specifying trakt client ID and secret via environment variables. --- resources/lib/traktapi.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/lib/traktapi.py b/resources/lib/traktapi.py index 116e0a5d..4e72f9ea 100644 --- a/resources/lib/traktapi.py +++ b/resources/lib/traktapi.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # import logging +import os import time from json import dumps, loads @@ -44,9 +45,16 @@ def __init__(self, force=False): Trakt.http.proxies = {"http": proxyURL, "https": proxyURL} # Configure + client_id = os.environ.get("TRAKT_CLIENT_ID") + client_secret = os.environ.get("TRAKT_CLIENT_SECRET") + + if not client_id or not client_secret: + client_id = deobfuscate(self.__client_id) + client_secret = deobfuscate(self.__client_secret) + Trakt.configuration.defaults.client( - id=deobfuscate(self.__client_id), - secret=deobfuscate(self.__client_secret), + id=client_id, + secret=client_secret, ) # Bind event From c6650dc44f5c6332196d00fa9d7def8c0645dadb Mon Sep 17 00:00:00 2001 From: sean Date: Wed, 11 Feb 2026 10:38:15 -0800 Subject: [PATCH 3/3] feat: add identifiable user agent --- resources/lib/traktapi.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/resources/lib/traktapi.py b/resources/lib/traktapi.py index 4e72f9ea..cd30d6de 100644 --- a/resources/lib/traktapi.py +++ b/resources/lib/traktapi.py @@ -57,6 +57,12 @@ def __init__(self, force=False): secret=client_secret, ) + user_agent = "Kodi script.trakt/%s" % __addonversion__ + if getattr(Trakt.http, "headers", None) is None: + Trakt.http.headers = {"User-Agent": user_agent} + else: + Trakt.http.headers["User-Agent"] = user_agent + # Bind event Trakt.on("oauth.token_refreshed", self.on_token_refreshed)