Files
cookie/cookie_cloud.py
T
2026-03-03 08:53:50 +08:00

177 lines
5.9 KiB
Python

"""Cookie Cloud 模块 - 从 Cookie Cloud 服务器获取 cookie 数据"""
import json
import hashlib
import base64
from typing import Dict, List, Optional, Tuple
import urllib.request
import urllib.error
from Crypto.Cipher import AES
from applogger import get_logger
logger = get_logger(__name__)
class CookieCloud:
"""Cookie Cloud 客户端"""
def __init__(self, api_url: str, uuid: str, password: str):
"""
初始化 Cookie Cloud 客户端
Args:
api_url: Cookie Cloud API 地址
uuid: 用户 UUID
password: 用户密码
"""
self.api_url = api_url.rstrip('/')
self.uuid = uuid
self.password = password
def _get_crypt_key(self) -> bytes:
"""生成加密密钥"""
combined_string = f"{self.uuid}-{self.password}"
return hashlib.md5(combined_string.encode('utf-8')).hexdigest()[:16].encode("utf-8")
def _bytes_to_key(self, data: bytes, salt: bytes, output: int = 48) -> bytes:
"""OpenSSL EVP_BytesToKey 密钥派生算法"""
assert len(salt) == 8, len(salt)
data += salt
key = hashlib.md5(data).digest()
final_key = key
while len(final_key) < output:
key = hashlib.md5(key + data).digest()
final_key += key
return final_key[:output]
def _decrypt(self, encrypted: str, passphrase: bytes) -> bytes:
"""解密数据"""
encrypted_bytes = base64.b64decode(encrypted)
assert encrypted_bytes.startswith(b"Salted__"), "Invalid encrypted data format"
salt = encrypted_bytes[8:16]
key_iv = self._bytes_to_key(passphrase, salt, 32 + 16)
key = key_iv[:32]
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
decrypted_padded = aes.decrypt(encrypted_bytes[16:])
padding_length = decrypted_padded[-1]
if isinstance(padding_length, str):
padding_length = ord(padding_length)
return decrypted_padded[:-padding_length]
def _download_data(self) -> Tuple[Optional[Dict], str]:
"""下载并解密所有数据"""
try:
url = f"{self.api_url}/get/{self.uuid}"
request = urllib.request.Request(
url,
headers={
'Content-Type': 'application/json',
'User-Agent': 'CookieCloud-Client/1.0'
},
method='GET'
)
response = urllib.request.urlopen(request, timeout=30)
if response.status != 200:
return None, f"服务器返回错误状态码: {response.status}"
result = json.loads(response.read().decode('utf-8'))
if not result:
return None, "服务器返回数据为空"
encrypted = result.get("encrypted")
if not encrypted:
return None, "未获取到cookie密文"
crypt_key = self._get_crypt_key()
try:
decrypted_data = self._decrypt(encrypted, crypt_key)
result = json.loads(decrypted_data.decode("utf-8"))
except Exception as e:
return None, f"cookie解密失败: {str(e)}"
if not result:
return None, "cookie解密为空"
return result, ""
except urllib.error.HTTPError as e:
return None, f"HTTP错误: {e.code} {e.reason}"
except urllib.error.URLError as e:
return None, f"网络连接失败: {e.reason}"
except Exception as e:
return None, f"下载失败: {str(e)}"
def get_cookies(self) -> Dict[str, List[Dict]]:
"""
从 Cookie Cloud 服务器获取 cookie 数据
Returns:
按域名分组的 cookie 数据字典
格式: {"domain.com": [{"name": "cookie1", "value": "val1", ...}, ...], ...}
"""
data, error = self._download_data()
if error:
logger.error(error)
return {}
# 处理数据结构
cookie_data = {}
# 兼容直接按域名分组的格式
if isinstance(data, dict) and not data.get('cookie_data'):
cookie_data = data
# 兼容包含 cookie_data 的格式
elif isinstance(data, dict) and data.get('cookie_data'):
cookie_data = data.get('cookie_data', {})
# 处理 sameSite 字段
processed_cookies = {}
for domain, cookies in cookie_data.items():
if not cookies:
continue
processed_cookies[domain] = []
for cookie in cookies:
if cookie.get('sameSite') == 'unspecified':
cookie['sameSite'] = 'Lax'
processed_cookies[domain].append(cookie)
return processed_cookies
def get_cookies_for_domain(self, domain: str) -> List[Dict]:
"""
获取指定域名的 cookie
Args:
domain: 域名
Returns:
cookie 列表
"""
all_cookies = self.get_cookies()
matched_cookies = []
for cookie_domain, cookies in all_cookies.items():
clean_domain = cookie_domain.lstrip('.')
if domain in clean_domain or clean_domain in domain:
matched_cookies.extend(cookies)
return matched_cookies
if __name__ == "__main__":
cc = CookieCloud(
api_url="https://movie-pilot.org/cookiecloud",
uuid="hu6n2vcUmzpu7mqUN2rVCg",
password="jtDM5dV9AyqXkZdQVeA9f6"
)
cookies = cc.get_cookies()
logger.info(f"获取到 {len(cookies)} 个域名的 cookies")
lmkbi_cookies = cc.get_cookies_for_domain('lmkbi.95155.com')
logger.info(f"获取到 {len(lmkbi_cookies)} 个 lmkbi.95155.com 的 cookies")