chore: 更新技能引用名称并新增 yt-studio-auth-prep 登录脚本

This commit is contained in:
2026-09-30 10:22:05 +08:00
parent 9020752eed
commit 05b2fc8d65
19 changed files with 513 additions and 162 deletions

View File

@@ -0,0 +1,142 @@
# -*- coding: utf-8 -*-
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "playwright>=1.40",
# ]
# ///
r"""
调试用户目录 .profile 生成 + YouTube 账号登录脚本(yt-studio-auth-prep 技能)。
职责单一:在指定目录创建(或复用)一个独立的浏览器用户数据目录,打开可见浏览器
窗口交给用户手动完成 Google/YouTube 登录,轮询确认 studio.youtube.com 不再跳转
登录页后关闭窗口退出。登录态持久化在该目录,供下游脚本复用:
- youtube-studio-csv-download : --user-data-dir "<place>/.profile" --channel <brand>
- yt-studio-groupid-lookup : 同上(在线捕获套件时)
用法(cwd 任意;目录不存在会自动创建):
python login_profile.py --channel chrome --user-data-dir "<place>/.profile"
python login_profile.py --channel msedge --user-data-dir "<place>/.profile" --timeout-seconds 600
注意:
- channel 一经选定,整个工作区所有下游命令必须用同一品牌(chrome/msedge);
换品牌 = 删除 .profile 后重跑本脚本,并重新捕获 bundles.json。
- 登录态失效(下游报跳 Google 登录页)时直接重跑本脚本补登录,.profile 保留原数据。
"""
from __future__ import annotations
import argparse
import os
import time
STUDIO_URL = "https://studio.youtube.com/"
DEFAULT_TIMEOUT_SECONDS = 600
def classify_url(url: str) -> str:
"""当前页面 URL 归类:google=登录页,studio=已进 Studio,other=其他。"""
u = (url or "").lower()
if "accounts.google" in u:
return "google"
if "studio.youtube.com" in u:
return "studio"
return "other"
def selftest():
assert classify_url("https://accounts.google.com/v3/signin/identifier?x=1") == "google"
assert classify_url("https://studio.youtube.com/channel/UCxx/analytics") == "studio"
assert classify_url("https://www.youtube.com/") == "other"
assert classify_url("") == "other"
print("selftest OK:URL 分类逻辑通过")
def wait_login(page, start_url: str, timeout_seconds: int) -> bool:
"""轮询等待用户完成登录;首次到达 studio 后回访一次,防会话弹回登录页。"""
page.goto(start_url, wait_until="domcontentloaded")
deadline = time.time() + timeout_seconds
hinted = False
last_report = time.time()
while time.time() < deadline:
kind = classify_url(page.url)
if kind == "studio":
page.wait_for_timeout(3000)
page.goto(start_url, wait_until="domcontentloaded")
if classify_url(page.url) == "studio":
return True
elif kind == "google":
if not hinted:
print("[登录] 请在打开的浏览器窗口中完成 Google/YouTube 登录"
"(支持两步验证),完成后本脚本自动检测……")
hinted = True
page.wait_for_timeout(2000)
else:
page.wait_for_timeout(3000)
page.goto(start_url, wait_until="domcontentloaded")
if time.time() - last_report >= 30:
print(f"[登录] 仍在等待登录……剩余 {max(0, int(deadline - time.time()))} 秒")
last_report = time.time()
return False
def main(argv=None):
ap = argparse.ArgumentParser(
description="创建调试用户目录 .profile 并完成 YouTube 账号登录")
ap.add_argument("--selftest", action="store_true",
help="仅跑 URL 分类自测(无需浏览器)")
ap.add_argument("--user-data-dir", help="调试用户目录路径,如 <place>/.profile")
ap.add_argument("--channel", choices=["chrome", "msedge"], default="chrome",
help="浏览器品牌,默认 %(default)s;选定后全工作区统一")
ap.add_argument("--start-url", default=STUDIO_URL,
help="登录检测起始页,默认 %(default)s")
ap.add_argument("--timeout-seconds", type=int, default=DEFAULT_TIMEOUT_SECONDS,
help="等待登录的最长秒数,默认 %(default)s")
args = ap.parse_args(argv)
if args.selftest:
selftest()
return
if not args.user_data_dir:
ap.error("--user-data-dir 必填,如 <place>/.profile")
os.makedirs(args.user_data_dir, exist_ok=True)
print(f"[登录] 打开 {args.channel}(profile={args.user_data_dir}),"
f"最长等待 {args.timeout_seconds} 秒……")
try:
from playwright.sync_api import sync_playwright
except ImportError:
raise SystemExit("[!] 缺少 playwright:在工作区(yt-download-place)内用"
" uv run python 重试,或先 uv sync。")
with sync_playwright() as p:
try:
context = p.chromium.launch_persistent_context(
user_data_dir=args.user_data_dir,
channel=args.channel,
headless=False,
args=["--disable-blink-features=AutomationControlled"],
)
except Exception as e: # noqa: BLE001
raise SystemExit(
f"[!] 启动浏览器失败: {e}\n"
f" 多为 {args.channel} 正被占用(上次窗口未关/手动开过 .profile)。"
"任务管理器彻底退出该品牌浏览器进程后重跑。")
try:
page = context.new_page()
ok = wait_login(page, args.start_url, args.timeout_seconds)
finally:
context.close()
if not ok:
raise SystemExit(
f"[!] {args.timeout_seconds} 秒内未完成登录。.profile 已保留进度,"
"直接重跑同一命令继续;等待不够就加大 --timeout-seconds。")
print(f"[登录] 登录成功:{args.user_data_dir}(channel={args.channel})")
print(f"[i] 该工作区所有下游命令统一使用 --channel {args.channel} "
f"--user-data-dir {args.user_data_dir}")
if __name__ == "__main__":
main()