[F] Fix mteam login

This commit is contained in:
2026-03-09 00:20:36 -04:00
parent b30200ae30
commit 6ec36b1555
+172 -108
View File
@@ -1,108 +1,172 @@
import sys import sys
import argparse import argparse
import pyotp import pyotp
from playwright.sync_api import sync_playwright from playwright.sync_api import sync_playwright
import tomllib import tomllib
from pathlib import Path from pathlib import Path
# --- CONFIGURATION --- # --- CONFIGURATION ---
LOGIN_URL = "https://kp.m-team.cc/login" LOGIN_URL = "https://kp.m-team.cc/login"
BASE_DIR = Path(__file__).parent BASE_DIR = Path(__file__).parent
USER_DATA_DIR = BASE_DIR / "data/browser_profile" USER_DATA_DIR = BASE_DIR / "data/browser_profile"
CONFIG_FILE = BASE_DIR / "config.toml" CONFIG_FILE = BASE_DIR / "config.toml"
def load_config(): def load_config():
if not CONFIG_FILE.exists(): return tomllib.loads(CONFIG_FILE.read_text())["m-team"]
print(f"Error: Configuration file '{CONFIG_FILE}' not found.")
sys.exit(1) def get_browser_context(p, headless=False):
return p.chromium.launch_persistent_context(user_data_dir=USER_DATA_DIR, headless=headless, channel="chrome")
# with CONFIG_FILE.open("rb") as f:
# config = tomllib.load(f) def ensure_logged_in(page):
config = tomllib.loads(CONFIG_FILE.read_text()) # 1. url is /login, 2. login form is visible
if page.url.startswith(LOGIN_URL) or page.is_visible("input#username"):
if "m-team" not in config: login(page, load_config())
print("Error: '[m-team]' section not found in config.toml") return True
sys.exit(1)
def login(page, config):
return config["m-team"] username = config.get("username")
password = config.get("password")
def run(): otp_key = config.get("otp_key")
config = load_config()
username = config.get("username") if not all([username, password, otp_key]):
password = config.get("password") print("Error: Missing username, password, or otp_key in config.toml")
otp_key = config.get("otp_key") sys.exit(1)
if not all([username, password, otp_key]): print(f"Navigating to {LOGIN_URL}...")
print("Error: Missing username, password, or otp_key in config.toml") page.goto(LOGIN_URL)
sys.exit(1) page.wait_for_load_state("networkidle")
print(f"Launching browser with persistent profile at: {USER_DATA_DIR}") # Check if we are already logged in (Login form not present)
if not page.is_visible("input#username"):
with sync_playwright() as p: print("Login form not found. You might already be logged in.")
# Launch a persistent context to save cookies print("Checking page title...")
browser = p.chromium.launch_persistent_context( print(f"Current Title: {page.title()}")
user_data_dir=USER_DATA_DIR, return
headless=False, # Set to True if you don't want to see the browser
channel="chrome", # Optional: Use 'msedge' or remove to use bundled chromium print("Login form detected. Attempting to log in...")
) page.fill("input#username", username)
page.fill("input#password", password)
page = browser.new_page() submit_selector = 'button[type="submit"]'
page.click(submit_selector)
print(f"Navigating to {LOGIN_URL}...") print("Credentials submitted. Waiting for OTP field...")
page.goto(LOGIN_URL) try:
page.wait_for_load_state("networkidle") page.wait_for_selector("input#otp-code", timeout=10000)
print("Generating OTP code from provided key...")
# Check if we are already logged in (Login form not present)
if not page.is_visible("input#username"): totp = pyotp.TOTP(otp_key.replace(" ", ""))
print("Login form not found. You might already be logged in.") current_otp = totp.now()
print("Checking page title...") print(f"Generated Code: {current_otp}")
print(f"Current Title: {page.title()}")
else: page.fill("input#otp-code", current_otp)
print("Login form detected. Attempting to log in...") page.press("input#otp-code", "Enter")
print("OTP Submitted.")
# 1. Fill Username except Exception as e:
page.fill("input#username", username) print(f"OTP field did not appear or an error occurred: {e}")
print("Maybe login failed or OTP wasn't required?")
# 2. Fill Password
page.fill("input#password", password) # Wait a moment to ensure login processes
page.wait_for_timeout(5000)
# 3. Click Submit
submit_selector = 'button[type="submit"]' print(f"Final URL: {page.url}")
page.click(submit_selector) print("Login process finished.")
print("Credentials submitted. Waiting for OTP field...") def get_torrents(page, imdb: str):
ensure_logged_in(page)
# 4. Handle OTP
try: if not imdb.startswith("https://www.imdb.com/title/"):
# Wait up to 10 seconds for the OTP input to appear imdb = f"https://www.imdb.com/title/{imdb}"
page.wait_for_selector("input#otpCode", timeout=10000)
url = f"https://kp.m-team.cc/mdb/title?source=imdb&imdb={urllib.parse.quote(imdb)}"
print("Generating OTP code from provided key...") print(f"Navigating to {url}...")
# Generate TOTP code using the secret key page.goto(url)
totp = pyotp.TOTP(otp_key.replace(" ", "")) # Sanitize spaces just in case page.wait_for_load_state("networkidle")
current_otp = totp.now()
print(f"Generated Code: {current_otp}")
# Fill the OTP
page.fill("input#otpCode", current_otp) def download(page, tid):
url = f"https://kp.m-team.cc/detail/{tid}"
# Press Enter to submit print(f"Navigating to {url}...")
page.press("input#otpCode", "Enter") page.goto(url)
page.wait_for_load_state("networkidle")
print("OTP Submitted.")
# Check if we are logged in (if we see the login form, we are not)
except Exception as e: if page.is_visible("input#username"):
print(f"OTP field did not appear or an error occurred: {e}") print("Error: Not logged in. Please run 'login' command first.")
print("Maybe login failed or OTP wasn't required?") return
# Wait a moment to ensure login processes try:
page.wait_for_timeout(5000) print("Looking for download button...")
# Button selector based on user request: <button ...><span>下載</span></button>
print(f"Final URL: {page.url}") # We use a role selector combined with name for robustness
print("Script finished. Cookies are saved in the profile folder.") download_button = page.get_by_role("button", name="下載")
# Close the browser if not download_button.is_visible():
browser.close() # Fallback to specific class if role text fails, though "下載" should work.
# The user provided class: ant-btn css-fjnik7 ant-btn-primary ant-btn-color-primary ant-btn-variant-solid
if __name__ == "__main__": # But classes like css-fjnik7 might be dynamic.
run() print("Download button not found by role/name. Trying generic selector...")
# Try a looser selector
download_button = page.locator("button:has-text('下載')")
if not download_button.is_visible():
print("Error: Download button not found on page.")
return
print("Clicking download button...")
with page.expect_download() as download_info:
download_button.click()
download = download_info.value
print(f"Download started: {download.suggested_filename}")
# Save to current directory
save_path = Path.cwd() / download.suggested_filename
download.save_as(save_path)
print(f"Successfully saved to: {save_path}")
except Exception as e:
print(f"An error occurred during download: {e}")
def main():
parser = argparse.ArgumentParser(description="M-Team Automation Tool")
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
# Login command
login_parser = subparsers.add_parser("login", help="Perform login")
# Download command
dl_parser = subparsers.add_parser("download", help="Download a torrent by TID")
dl_parser.add_argument("tid", help="Torrent ID")
args = parser.parse_args()
# Default to login if no command provided (backward compatibility behavior)
if not args.command:
print("No command specified, defaulting to 'login'.")
command = "login"
else:
command = args.command
config = load_config()
print(f"Launching browser with persistent profile at: {USER_DATA_DIR}")
with sync_playwright() as p:
context = get_browser_context(p, headless=False)
# Persistent context might have an existing page or we create one
if len(context.pages) > 0:
page = context.pages[0]
else:
page = context.new_page()
if command == "login":
login(page, config)
elif command == "download":
download(page, args.tid)
context.close()
if __name__ == "__main__":
main()