# Douyin Login Entry Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add a dedicated browser-login launcher and a clearer attach-port check so the Douyin crawler has a stable two-step workflow: login first, crawl second.

**Architecture:** Keep browser-launch responsibilities in a new `login_douyin.py` script and keep crawl responsibilities in `Douyin.py`. Add a small socket-based port readiness check before attaching to Chrome, and cover the new behavior with unit tests before implementing production code.

**Tech Stack:** Python 3, `argparse`, `pathlib`, `subprocess`, `socket`, `unittest`

---

### Task 1: Write failing tests for the new login launcher

**Files:**
- Create: `login_douyin.py`
- Create: `test_login_douyin.py`

- [ ] **Step 1: Write the failing test**

```python
def test_build_login_command_uses_expected_chrome_arguments(self) -> None:
    module = importlib.import_module("login_douyin")
    command = module.build_login_command(
        chrome_path="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
        profile_dir=Path("/tmp/douyin-profile"),
        browser_port=9223,
        user_url="https://www.douyin.com/user/example",
    )
    self.assertEqual(
        command,
        [
            "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
            "--user-data-dir=/tmp/douyin-profile",
            "--remote-debugging-port=9223",
            "https://www.douyin.com/user/example",
        ],
    )
```

- [ ] **Step 2: Run test to verify it fails**

Run: `./.venv/bin/python -m unittest test_login_douyin.py -v`
Expected: FAIL because `login_douyin.py` does not exist yet.

- [ ] **Step 3: Write minimal implementation**

Create `login_douyin.py` with:

- `DEFAULT_CHROME_PATH`
- `DEFAULT_BROWSER_PORT = 9223`
- `DEFAULT_PROFILE_DIR`
- `build_login_command(...)`

- [ ] **Step 4: Run test to verify it passes**

Run: `./.venv/bin/python -m unittest test_login_douyin.py -v`
Expected: PASS for the command-building test.

- [ ] **Step 5: Commit**

Not applicable here because the workspace is not a git repository.

### Task 2: Add tests and implementation for launcher validation and user guidance

**Files:**
- Modify: `login_douyin.py`
- Modify: `test_login_douyin.py`

- [ ] **Step 1: Write the failing tests**

Add tests for:

- parser defaults use `9223`
- `main()` creates the profile dir
- `main()` prints the follow-up crawl command
- `main()` returns non-zero with a readable message when the Chrome path does not exist

- [ ] **Step 2: Run tests to verify they fail**

Run: `./.venv/bin/python -m unittest test_login_douyin.py -v`
Expected: FAIL because validation and guidance behavior is not implemented yet.

- [ ] **Step 3: Write minimal implementation**

Add to `login_douyin.py`:

- `build_parser()`
- `launch_browser(...)`
- `main(...)`
- readable `SystemExit`/stderr-style messaging through printed output and return codes

- [ ] **Step 4: Run tests to verify they pass**

Run: `./.venv/bin/python -m unittest test_login_douyin.py -v`
Expected: PASS

- [ ] **Step 5: Commit**

Not applicable here because the workspace is not a git repository.

### Task 3: Write failing tests for attach-port readiness in the crawler

**Files:**
- Modify: `Douyin.py`
- Modify: `test_douyin.py`

- [ ] **Step 1: Write the failing tests**

Add tests for:

- `ensure_browser_debug_port_ready()` returns successfully when a temporary local server is listening
- `ensure_browser_debug_port_ready()` raises a readable `RuntimeError` when the port is unavailable

- [ ] **Step 2: Run tests to verify they fail**

Run: `./.venv/bin/python -m unittest test_douyin.py -v`
Expected: FAIL because the function does not exist yet.

- [ ] **Step 3: Write minimal implementation**

Add to `Douyin.py`:

- socket-based readiness helper
- call it in `collect_videos()` before `create_page(...)` when `browser_port` is provided

- [ ] **Step 4: Run tests to verify they pass**

Run: `./.venv/bin/python -m unittest test_douyin.py -v`
Expected: PASS

- [ ] **Step 5: Commit**

Not applicable here because the workspace is not a git repository.

### Task 4: Update usage documentation

**Files:**
- Modify: `抖音爬取视频.md`

- [ ] **Step 1: Write the failing doc expectation**

Define the required doc updates:

- explicit step 1 command for `login_douyin.py`
- explicit step 2 command for `Douyin.py --browser-port 9223`
- short note that login state is kept in the dedicated profile dir

- [ ] **Step 2: Verify current doc is incomplete**

Run: `rg -n "login_douyin.py|--browser-port 9223" 抖音爬取视频.md`
Expected: no matches or incomplete guidance

- [ ] **Step 3: Write minimal documentation update**

Append a short “推荐流程” section to `抖音爬取视频.md`.

- [ ] **Step 4: Verify the doc contains the new commands**

Run: `rg -n "login_douyin.py|--browser-port 9223" 抖音爬取视频.md`
Expected: matches for both commands

- [ ] **Step 5: Commit**

Not applicable here because the workspace is not a git repository.

### Task 5: Run full verification

**Files:**
- Modify: `Douyin.py`
- Modify: `login_douyin.py`
- Modify: `test_douyin.py`
- Modify: `test_login_douyin.py`
- Modify: `抖音爬取视频.md`

- [ ] **Step 1: Run the full unit test suite**

Run: `./.venv/bin/python -m unittest test_douyin.py test_login_douyin.py -v`
Expected: all tests pass

- [ ] **Step 2: Run the login launcher manually**

Run: `./.venv/bin/python login_douyin.py --browser-port 9223`
Expected: visible Chrome launches and prints the next crawl command

- [ ] **Step 3: Run the crawler against the logged-in browser**

Run: `./.venv/bin/python Douyin.py --pages 1 --timeout 20 --browser-port 9223`
Expected: videos are downloaded to `video/`

- [ ] **Step 4: Review changed files for scope drift**

Run: `rg --files`
Expected: only the planned files changed or were added

- [ ] **Step 5: Commit**

Not applicable here because the workspace is not a git repository.
