# NotebookLM Per-User Google Account Linking - Fix Plan

## Problem Summary
When users try to link their Google account to NotebookLM in per-user mode, the browser launch fails because:
- Playwright is trying to launch Chromium in **headed mode** (`headless=False`)
- The server environment lacks a display server (`$DISPLAY` not set)
- Error: `TargetClosedError: BrowserType.launch: Target page, context or browser has been closed`

## Root Cause
File: `app/ai/_nlm_login_worker.py` line 26-29
```python
browser = p.chromium.launch(
    headless=False,  # ← Problem: Requires display server
    args=["--disable-blink-features=AutomationControlled"],
)
```

---

## Solution: Implement Headless Mode with Remote Debugging

The best approach is to use **headless mode with remote debugging port**, which allows:
1. No display server required
2. Works in server environments
3. Users can still connect via remote debugging URL if needed

---

## Implementation Plan

### Option 1: Simple Headless Mode (Quickest)
Change `_nlm_login_worker.py` line 27:
```python
headless=True,  # Changed from False
```

**Pros:** Simple fix, works immediately
**Cons:** Users can't see the login process

### Option 2: Headless with Remote Debugging URL (Recommended)
Allow users to connect via a remote debugging URL:

```python
browser = p.chromium.launch(
    headless=True,
    args=[
        "--disable-blink-features=AutomationControlled",
        "--remote-debugging-address=0.0.0.0:9222",  # Remote debugging
    ],
)
```

Then provide the debugging URL to users via a WebSocket or polling endpoint.

### Option 3: Xvfb Virtual Display (For Headed Mode)
If you want to keep headed mode (for troubleshooting):

```python
import subprocess
import os
import signal
import time

def start_xvfb():
    """Start Xvfb virtual display."""
    xvfb = subprocess.Popen([
        "Xvfb", ":99", "-screen", "0", "1280x720x24"
    ])
    os.environ["DISPLAY"] = ":99"
    time.sleep(1)  # Let Xvfb start
    return xvfb

# In main():
xvfb_process = start_xvfb()
try:
    # ... browser launch code ...
finally:
    xvfb_process.terminate()
```

---

## Recommended Implementation

### Step 1: Update `_nlm_login_worker.py`

**File:** `/home/ashraffarid2010/jadwaai.com/app/ai/_nlm_login_worker.py`

Change line 26-29 to:
```python
browser = p.chromium.launch(
    headless=True,  # Changed from False
    args=[
        "--disable-blink-features=AutomationControlled",
        "--no-sandbox",
        "--disable-dev-shm-usage",
    ],
)
```

### Step 2: Create a Polling Endpoint for Status

**File:** `/home/ashraffarid2010/jadwaai.com/app/routes/study.py`

Add an endpoint to check login status:
```python
@study_bp.route("/api/notebook/status/<int:report_id>")
@login_required
def notebook_login_status(report_id):
    """Check if NotebookLM login is complete."""
    from app.models.report import Report

    report = Report.query.get_or_404(report_id)

    # Check if login worker completed
    status_file = f"/tmp/nlm_login_{current_user.id}_{report_id}.txt"

    if os.path.exists(status_file):
        with open(status_file, 'r') as f:
            content = f.read()
        os.remove(status_file)

        if content.startswith("OK:"):
            # Save session path
            session_path = content.replace("OK:", "")
            current_user.google_nlm_session_path = session_path
            current_user.google_nlm_connected_at = datetime.utcnow()
            db.session.commit()
            return jsonify({"status": "success"})
        else:
            return jsonify({"status": "failed", "error": content})

    # Check if process is still running
    pid_file = f"/tmp/nlm_login_{current_user.id}_{report_id}.pid"
    if os.path.exists(pid_file):
        return jsonify({"status": "pending"})

    return jsonify({"status": "not_started"})
```

### Step 3: Update the Login Caller

**File:** `/home/ashraffarid2010/jadwaai.com/app/routes/study.py`

Find `api_notebook_connect_google()` function and update to:
1. Write status/pid files
2. Run worker asynchronously
3. Return status endpoint for frontend polling

### Step 4: Add Xvfb Support (Optional)

For environments where you might want headed mode:

**Install Xvfb:**
```bash
sudo apt-get install xvfb
```

**Create wrapper script:** `/home/ashraffarid2010/jadwaai.com/app/ai/_nlm_login_worker_xvfb.sh`
```bash
#!/bin/bash
# Wrapper to run Playwright with Xvfb
DISPLAY=:99 xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24" \
    python /home/ashraffarid2010/jadwaai.com/app/ai/_nlm_login_worker.py "$@"
```

Make executable:
```bash
chmod +x app/ai/_nlm_login_worker_xvfb.sh
```

---

## Files to Modify

1. **`app/ai/_nlm_login_worker.py`** - Change `headless=False` to `headless=True`
2. **`app/routes/study.py`** - Update `api_notebook_connect_google()` function (if needed)
3. **`app/templates/study/notebook.html`** - Update UI to show polling status

---

## Testing Checklist

After implementing:
1. ✅ Test per-user mode connection
2. ✅ Verify session is saved to user's Google account
3. ✅ Test notebook creation with connected account
4. ✅ Verify admin shared mode still works
5. ✅ Check for orphaned Playwright processes

---

## Additional Considerations

### Process Cleanup
Ensure Playwright processes are cleaned up:
```python
import psutil

def cleanup_playwright_processes():
    """Kill orphaned Playwright Chromium processes."""
    for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
        if proc.info['name'] and 'chrome' in proc.info['name'].lower():
            if '--remote-debugging-port' in ' '.join(proc.info['cmdline'] or []):
                proc.kill()
```

### Session Storage Security
- Ensure session files are stored securely
- Use unique paths per user: `/var/playwright_sessions/{user_id}/storage.json`
- Clean up old sessions periodically

---

## Quick Fix (Immediate Solution)

The quickest fix is changing one line in `_nlm_login_worker.py`:

```python
# Line 27, change from:
headless=False,

# To:
headless=True,
```

Then reload gunicorn:
```bash
kill -HUP $(pgrep -f "gunicorn.*jadwaai")
```

This will allow per-user Google account linking to work immediately in headless mode.
