From 9345cd850127d92b11754d5927251a70ee80266e Mon Sep 17 00:00:00 2001 From: Pari Malam Date: Fri, 29 May 2026 21:28:34 +0800 Subject: [PATCH] + add coloredlogs --- .gitignore | 1 + config.ini.example | 3 +++ main.py | 21 ++++++++++---------- modules/__init__.py | 5 ----- modules/helpers.py | 6 +++--- modules/logging.py | 47 +++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 19 ++++++++++++++++++ 7 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 config.ini.example create mode 100644 modules/logging.py diff --git a/.gitignore b/.gitignore index 423ea9c..1c48eb5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ devices/ config.ini cookies/* venv/* +config.ini output/* .vscode/* modules/__pycache__/ diff --git a/config.ini.example b/config.ini.example new file mode 100644 index 0000000..e27f9d9 --- /dev/null +++ b/config.ini.example @@ -0,0 +1,3 @@ +[NETFLIX] +EMAIL = +PASSWORD = \ No newline at end of file diff --git a/main.py b/main.py index 9f6c350..f4d3540 100644 --- a/main.py +++ b/main.py @@ -24,10 +24,9 @@ from modules.helpers import ( build_msl_trace_event, extract_key_id_from_mastertoken, request_args_to_dict, ) from modules.config import setup_config +from modules.logging import setup_logger -logging.basicConfig(level=logging.INFO, format="%(name)s - %(levelname)s - %(message)s") -log = logging.getLogger("MSL HANDSHAKE") - +log = setup_logger("MSL HANDSHAKE") config = setup_config() EMAIL = config["NETFLIX"]["EMAIL"] PASSWORD = config["NETFLIX"]["PASSWORD"] @@ -46,7 +45,7 @@ def setup_session(verify_tls: bool = True) -> requests.Session: # ====================================================================== def run_android_rsa(new_msl: bool = False, no_verify: bool = False): - logger = logging.getLogger('ANDROID MSL RSA') + logger = setup_logger('ANDROID MSL RSA') output_dir = ensure_output_dir("android") msl_cache_path = output_dir / "msl_keys_cache_android_rsa.json" auth_cookies_path = output_dir / "netflix_auth_cookies_rsa.json" @@ -224,7 +223,7 @@ def run_android_rsa(new_msl: bool = False, no_verify: bool = False): def run_android(wvd_path: Path, new_msl: bool = False, no_verify: bool = False): - log = logging.getLogger('ANDROID MSL') + log = setup_logger('ANDROID MSL') OUTPUT_DIR = ensure_output_dir("android") MSL_CACHE_PATH = OUTPUT_DIR / "msl_keys_cache_android.json" AUTH_COOKIES_PATH = OUTPUT_DIR / "netflix_auth_cookies.json" @@ -517,7 +516,7 @@ def run_android(wvd_path: Path, def run_ios(wvd_path: Path, new_msl: bool = False, no_verify: bool = False): - log = logging.getLogger('IOS MSL') + log = setup_logger('IOS MSL') OUTPUT_DIR = ensure_output_dir("ios") MSL_CACHE_PATH = OUTPUT_DIR / "msl_keys_cache_ios.json" @@ -834,7 +833,7 @@ def run_ios(wvd_path: Path, def run_tv(wvd_path: Path, new_msl: bool = False, no_verify: bool = False): - log = logging.getLogger('ANDROID TV MSL') + log = setup_logger('ANDROID TV MSL') OUTPUT_DIR = ensure_output_dir("tv") MSL_CACHE_PATH = OUTPUT_DIR / "msl_keys_cache.json" @@ -858,7 +857,7 @@ def run_tv(wvd_path: Path, NETJS_VERSION = "3.0.5" APK_VERSION = "12.1.9" UI_SEM_VER = "44798.0.0" - ESN = f"{DEVICE_TYPE}-11233-{generate_esn_random_suffix(64)}" + ESN = f"{DEVICE_TYPE}-11233-{''.join(random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for _ in range(64))}" IMPORTANT_COOKIE_NAMES = ( "netflix-mfa-nonce", @@ -1875,7 +1874,7 @@ def run_tv(wvd_path: Path, # ====================================================================== def run_tv_otp(wvd_path: Path, new_msl: bool = False, no_verify: bool = False): - log = logging.getLogger('ANDROID TV MSL') + log = setup_logger('ANDROID TV MSL') OUTPUT_DIR = ensure_output_dir() MSL_CACHE_PATH = OUTPUT_DIR / "msl_keys_cache.json" @@ -3031,7 +3030,7 @@ def run_tv_otp(wvd_path: Path, new_msl: bool = False, no_verify: bool = False): def run_web(new_msl: bool = False, no_verify: bool = False, recaptcha_token: str = ''): - log = logging.getLogger('BROWSER MSL') + log = setup_logger('BROWSER MSL') OUTPUT_DIR = ensure_output_dir("browser") MSL_CACHE_PATH = OUTPUT_DIR / "msl_keys_cache_web.json" @@ -3491,7 +3490,7 @@ def run_web(new_msl: bool = False, no_verify: bool = False, def run_mgk(kpekph_path: Optional[Path], esnid: str, new_msl: bool = False): - log = logging.getLogger('MGK MSL') + log = setup_logger('MGK MSL') OUTPUT_DIR = ensure_output_dir("mgk") MSL_CACHE_PATH = OUTPUT_DIR / "msl_keys_cache_mgk.json" diff --git a/modules/__init__.py b/modules/__init__.py index d890222..e69de29 100644 --- a/modules/__init__.py +++ b/modules/__init__.py @@ -1,5 +0,0 @@ -from .msl_android import MSL_ANDROID -from .msl_ios import MSL_IOS -from .msl_tv import MSL_TV -from .msl_web import MSL_WEB -from .msl_mgk import MSL_MGK diff --git a/modules/helpers.py b/modules/helpers.py index f0845c3..e79f9a1 100644 --- a/modules/helpers.py +++ b/modules/helpers.py @@ -29,11 +29,11 @@ from Crypto.Util.Padding import unpad def ensure_output_dir(platform_name, base_dir: Optional[Path] = None) -> Path: """Create (if needed) and return the ``output`` directory under *base_dir*. - If *base_dir* is ``None`` it defaults to the directory that contains the - calling script (i.e. ``Path(__file__).resolve().parent``). + If *base_dir* is ``None`` it defaults to the project root directory + (parent of the ``modules`` directory). """ if base_dir is None: - base_dir = Path(__file__).resolve().parent + base_dir = Path(__file__).resolve().parent.parent output_dir = base_dir / "output" / platform_name output_dir.mkdir(parents=True, exist_ok=True) return output_dir diff --git a/modules/logging.py b/modules/logging.py new file mode 100644 index 0000000..bde7d37 --- /dev/null +++ b/modules/logging.py @@ -0,0 +1,47 @@ +""" +logging.py — Colored logging setup for the NF-MSL project. +""" + +from __future__ import annotations + +import logging +from typing import Optional + +try: + import coloredlogs + COLOREDLOGS_AVAILABLE = True +except ImportError: + COLOREDLOGS_AVAILABLE = False + + +def setup_logger( + name: str, + level: int = logging.INFO, + fmt: Optional[str] = None, +) -> logging.Logger: + """Create and return a logger with optional colored output. + + If coloredlogs is installed, the logger will use colored output. + Otherwise, falls back to standard logging with the specified format. + """ + logger = logging.getLogger(name) + if logger.handlers: + return logger + + if fmt is None: + fmt = "%(name)s - %(levelname)s - %(message)s" + + if COLOREDLOGS_AVAILABLE: + coloredlogs.install( + level=level, + fmt=fmt, + logger=logger, + reconfigure=True, + ) + else: + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter(fmt)) + logger.addHandler(handler) + logger.setLevel(level) + + return logger \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2fea42f..0519cd2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,35 @@ +aiohappyeyeballs==2.6.2 +aiohttp==3.13.5 +aiosignal==1.4.0 +async-timeout==5.0.1 +attrs==26.1.0 certifi==2026.5.20 +cffi==2.0.0 charset-normalizer==3.4.7 click==8.4.1 colorama==0.4.6 +coloredlogs==15.0.1 construct==2.8.8 +cryptography==45.0.7 +ECPy==1.2.5 +frozenlist==1.8.0 +humanfriendly==10.0 idna==3.17 jsonpickle==4.1.2 +multidict==6.7.1 +platformdirs==4.10.0 +propcache==0.5.2 protobuf==6.33.6 +pycparser==3.0 pycryptodome==3.23.0 pycryptodomex==3.23.0 pymp4==1.4.0 +pyplayready==0.8.4 +pyreadline3==3.5.6 pywidevine==1.9.0 PyYAML==6.0.3 requests==2.34.2 +typing_extensions==4.15.0 Unidecode==1.4.0 urllib3==2.7.0 +yarl==1.24.2