dsora-gen 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dsora_gen-0.1.0/.claude/settings.json +8 -0
- dsora_gen-0.1.0/.gitignore +30 -0
- dsora_gen-0.1.0/PKG-INFO +116 -0
- dsora_gen-0.1.0/README.md +105 -0
- dsora_gen-0.1.0/pyproject.toml +22 -0
- dsora_gen-0.1.0/run.sh +32 -0
- dsora_gen-0.1.0/sora_video_tool/.gitignore +18 -0
- dsora_gen-0.1.0/sora_video_tool/README.md +106 -0
- dsora_gen-0.1.0/sora_video_tool/__init__.py +3 -0
- dsora_gen-0.1.0/sora_video_tool/api_client.py +49 -0
- dsora_gen-0.1.0/sora_video_tool/assets/tshirt.png +0 -0
- dsora_gen-0.1.0/sora_video_tool/browser_launcher.py +147 -0
- dsora_gen-0.1.0/sora_video_tool/cli.py +355 -0
- dsora_gen-0.1.0/sora_video_tool/config.py +39 -0
- dsora_gen-0.1.0/sora_video_tool/constants.py +40 -0
- dsora_gen-0.1.0/sora_video_tool/profile_manager.py +29 -0
- dsora_gen-0.1.0/sora_video_tool/run_tracker.py +61 -0
- dsora_gen-0.1.0/sora_video_tool/sora_bot.py +275 -0
- dsora_gen-0.1.0/sora_video_tool/telegram_notifier.py +68 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Virtual environment
|
|
2
|
+
.venv/
|
|
3
|
+
venv/
|
|
4
|
+
|
|
5
|
+
# Chrome profile data (session/cookies — KHONG commit len git)
|
|
6
|
+
sora_video_tool/profiles/
|
|
7
|
+
|
|
8
|
+
# App data (settings, run log, profiles cua user sau khi cai)
|
|
9
|
+
~/.sora-tool/
|
|
10
|
+
|
|
11
|
+
# Python cache
|
|
12
|
+
__pycache__/
|
|
13
|
+
*.pyc
|
|
14
|
+
*.pyo
|
|
15
|
+
*.pyd
|
|
16
|
+
*.pdb
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
|
|
19
|
+
# Build artifacts
|
|
20
|
+
dist/
|
|
21
|
+
build/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
|
|
24
|
+
# macOS
|
|
25
|
+
.DS_Store
|
|
26
|
+
.AppleDouble
|
|
27
|
+
|
|
28
|
+
# Temp files
|
|
29
|
+
*.tmp
|
|
30
|
+
*.log
|
dsora_gen-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dsora-gen
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Tu dong tao video Sora theo batch voi nhieu Chrome profiles
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: automation,playwright,sora,video
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: click>=8.0.0
|
|
9
|
+
Requires-Dist: playwright>=1.40.0
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# Sora Video Tool
|
|
13
|
+
|
|
14
|
+
Tu dong tao video Sora theo batch voi nhieu Chrome profiles.
|
|
15
|
+
|
|
16
|
+
## Cai dat
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Clone repo va vao thu muc
|
|
20
|
+
cd auto-gen-video-sora
|
|
21
|
+
|
|
22
|
+
# Cach 1: dung run.sh (tu dong cai dat moi thu)
|
|
23
|
+
chmod +x run.sh
|
|
24
|
+
./run.sh setup
|
|
25
|
+
|
|
26
|
+
# Cach 2: thu cong
|
|
27
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
28
|
+
pip install -e .
|
|
29
|
+
playwright install chromium # chi can cho Playwright, khong phai cho Chrome thuc
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Su dung
|
|
33
|
+
|
|
34
|
+
### Thiet lap lan dau
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
./run.sh setup
|
|
38
|
+
# hoac
|
|
39
|
+
sora setup
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Flow:
|
|
43
|
+
1. Nhap so luong profile
|
|
44
|
+
2. Nhap ten alias cho tung profile
|
|
45
|
+
3. Nhap username dsora
|
|
46
|
+
4. Nhap backend key
|
|
47
|
+
5. Nhap Telegram bot token / channel ID (tuy chon)
|
|
48
|
+
6. Chrome mo ra -> dang nhap Google + Sora + cau hinh Settings
|
|
49
|
+
|
|
50
|
+
### Dang nhap lai mot profile cu
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
sora setup --retry
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Chay tu dong
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
./run.sh run
|
|
60
|
+
# hoac
|
|
61
|
+
sora run
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Tool se:
|
|
65
|
+
- Lay danh sach san pham tu API
|
|
66
|
+
- Chay lan luot tung profile (5 san pham / profile)
|
|
67
|
+
- Gui thong bao Telegram sau moi san pham / profile
|
|
68
|
+
- Luu lich su chay vao `~/.sora-tool/run_log.json`
|
|
69
|
+
|
|
70
|
+
### Xem huong dan
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
sora help
|
|
74
|
+
sora --help
|
|
75
|
+
sora run --help
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Cau truc thu muc
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
auto-gen-video-sora/
|
|
82
|
+
├── pyproject.toml # Cau hinh PyPI
|
|
83
|
+
├── run.sh # Script khoi dong nhanh
|
|
84
|
+
├── README.md
|
|
85
|
+
├── .gitignore
|
|
86
|
+
└── sora_video_tool/ # Python package
|
|
87
|
+
├── cli.py # Entry point (sora setup / run / help)
|
|
88
|
+
├── constants.py # Hang so toan cuc
|
|
89
|
+
├── config.py # Doc/ghi ~/.sora-tool/settings.json
|
|
90
|
+
├── profile_manager.py # Quan ly thu muc Chrome profile
|
|
91
|
+
├── browser_launcher.py # Mo Chrome (login + CDP automation)
|
|
92
|
+
├── sora_bot.py # Logic tu dong hoa Sora
|
|
93
|
+
├── api_client.py # Giao tiep voi backend API
|
|
94
|
+
├── telegram_notifier.py # Gui thong bao Telegram
|
|
95
|
+
├── run_tracker.py # Theo doi lich su chay
|
|
96
|
+
└── assets/
|
|
97
|
+
└── tshirt.png # Anh mac dinh
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Du lieu luu tru
|
|
101
|
+
|
|
102
|
+
Tat ca du lieu nguoi dung luu tai `~/.sora-tool/`:
|
|
103
|
+
|
|
104
|
+
| File | Noi dung |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `settings.json` | Username, backend key, telegram, profiles |
|
|
107
|
+
| `run_log.json` | Lich su chay tung profile |
|
|
108
|
+
| `profiles/profile_1/` | Chrome user data (session, cookies) |
|
|
109
|
+
|
|
110
|
+
## Publish len PyPI
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
pip install build twine
|
|
114
|
+
python -m build
|
|
115
|
+
twine upload dist/*
|
|
116
|
+
```
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Sora Video Tool
|
|
2
|
+
|
|
3
|
+
Tu dong tao video Sora theo batch voi nhieu Chrome profiles.
|
|
4
|
+
|
|
5
|
+
## Cai dat
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Clone repo va vao thu muc
|
|
9
|
+
cd auto-gen-video-sora
|
|
10
|
+
|
|
11
|
+
# Cach 1: dung run.sh (tu dong cai dat moi thu)
|
|
12
|
+
chmod +x run.sh
|
|
13
|
+
./run.sh setup
|
|
14
|
+
|
|
15
|
+
# Cach 2: thu cong
|
|
16
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
17
|
+
pip install -e .
|
|
18
|
+
playwright install chromium # chi can cho Playwright, khong phai cho Chrome thuc
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Su dung
|
|
22
|
+
|
|
23
|
+
### Thiet lap lan dau
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
./run.sh setup
|
|
27
|
+
# hoac
|
|
28
|
+
sora setup
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Flow:
|
|
32
|
+
1. Nhap so luong profile
|
|
33
|
+
2. Nhap ten alias cho tung profile
|
|
34
|
+
3. Nhap username dsora
|
|
35
|
+
4. Nhap backend key
|
|
36
|
+
5. Nhap Telegram bot token / channel ID (tuy chon)
|
|
37
|
+
6. Chrome mo ra -> dang nhap Google + Sora + cau hinh Settings
|
|
38
|
+
|
|
39
|
+
### Dang nhap lai mot profile cu
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
sora setup --retry
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Chay tu dong
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
./run.sh run
|
|
49
|
+
# hoac
|
|
50
|
+
sora run
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Tool se:
|
|
54
|
+
- Lay danh sach san pham tu API
|
|
55
|
+
- Chay lan luot tung profile (5 san pham / profile)
|
|
56
|
+
- Gui thong bao Telegram sau moi san pham / profile
|
|
57
|
+
- Luu lich su chay vao `~/.sora-tool/run_log.json`
|
|
58
|
+
|
|
59
|
+
### Xem huong dan
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
sora help
|
|
63
|
+
sora --help
|
|
64
|
+
sora run --help
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Cau truc thu muc
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
auto-gen-video-sora/
|
|
71
|
+
├── pyproject.toml # Cau hinh PyPI
|
|
72
|
+
├── run.sh # Script khoi dong nhanh
|
|
73
|
+
├── README.md
|
|
74
|
+
├── .gitignore
|
|
75
|
+
└── sora_video_tool/ # Python package
|
|
76
|
+
├── cli.py # Entry point (sora setup / run / help)
|
|
77
|
+
├── constants.py # Hang so toan cuc
|
|
78
|
+
├── config.py # Doc/ghi ~/.sora-tool/settings.json
|
|
79
|
+
├── profile_manager.py # Quan ly thu muc Chrome profile
|
|
80
|
+
├── browser_launcher.py # Mo Chrome (login + CDP automation)
|
|
81
|
+
├── sora_bot.py # Logic tu dong hoa Sora
|
|
82
|
+
├── api_client.py # Giao tiep voi backend API
|
|
83
|
+
├── telegram_notifier.py # Gui thong bao Telegram
|
|
84
|
+
├── run_tracker.py # Theo doi lich su chay
|
|
85
|
+
└── assets/
|
|
86
|
+
└── tshirt.png # Anh mac dinh
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Du lieu luu tru
|
|
90
|
+
|
|
91
|
+
Tat ca du lieu nguoi dung luu tai `~/.sora-tool/`:
|
|
92
|
+
|
|
93
|
+
| File | Noi dung |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `settings.json` | Username, backend key, telegram, profiles |
|
|
96
|
+
| `run_log.json` | Lich su chay tung profile |
|
|
97
|
+
| `profiles/profile_1/` | Chrome user data (session, cookies) |
|
|
98
|
+
|
|
99
|
+
## Publish len PyPI
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pip install build twine
|
|
103
|
+
python -m build
|
|
104
|
+
twine upload dist/*
|
|
105
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dsora-gen"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Tu dong tao video Sora theo batch voi nhieu Chrome profiles"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
keywords = ["sora", "automation", "video", "playwright"]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"playwright>=1.40.0",
|
|
15
|
+
"click>=8.0.0",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
dsg = "sora_video_tool.cli:main"
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["sora_video_tool"]
|
dsora_gen-0.1.0/run.sh
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env zsh
|
|
2
|
+
# ----------------------------------------------------------------------------
|
|
3
|
+
# run.sh — Khoi dong Sora Video Tool
|
|
4
|
+
# Su dung: ./run.sh setup | ./run.sh run | ./run.sh help
|
|
5
|
+
# ----------------------------------------------------------------------------
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
VENV="$ROOT/.venv"
|
|
10
|
+
PYTHON="$VENV/bin/python"
|
|
11
|
+
PIP="$VENV/bin/pip"
|
|
12
|
+
|
|
13
|
+
# 1. Tao virtualenv neu chua co
|
|
14
|
+
if [ ! -f "$PYTHON" ]; then
|
|
15
|
+
echo "[run.sh] Tao virtual environment ..."
|
|
16
|
+
python3 -m venv "$VENV"
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# 2. Cai dependencies
|
|
20
|
+
echo "[run.sh] Kiem tra dependencies ..."
|
|
21
|
+
"$PIP" install -q --upgrade pip
|
|
22
|
+
"$PIP" install -q -e "$ROOT"
|
|
23
|
+
|
|
24
|
+
# 3. Cai Playwright browsers neu chua co
|
|
25
|
+
if [ ! -d "$HOME/Library/Caches/ms-playwright" ] && [ ! -d "$HOME/.cache/ms-playwright" ]; then
|
|
26
|
+
echo "[run.sh] Cai Playwright browsers ..."
|
|
27
|
+
"$VENV/bin/playwright" install chromium
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# 4. Chay lenh — truyen tat ca tham so
|
|
31
|
+
echo "[run.sh] Chay: dsg $*"
|
|
32
|
+
"$VENV/bin/dsg" "$@"
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# sora_video_tool
|
|
2
|
+
|
|
3
|
+
Manage multiple isolated Chrome profiles and automate Sora video generation.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Project structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
sora_video_tool/
|
|
11
|
+
├── main.py # CLI entry point
|
|
12
|
+
├── config.py # Global settings (URLs, paths, browser options)
|
|
13
|
+
├── profile_manager.py # Create / list persistent profile directories
|
|
14
|
+
├── browser_launcher.py# Launch Chromium with persistent contexts
|
|
15
|
+
├── sora_bot.py # Automation logic (stubs – fill in when ready)
|
|
16
|
+
├── data_provider.py # Mock prompt/image data source
|
|
17
|
+
├── requirements.txt
|
|
18
|
+
└── profiles/ # Auto-created; one sub-folder per profile
|
|
19
|
+
├── profile_1/
|
|
20
|
+
├── profile_2/
|
|
21
|
+
└── ...
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 1 — Install dependencies
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# (Recommended) create a virtual environment first
|
|
30
|
+
python -m venv .venv
|
|
31
|
+
source .venv/bin/activate # macOS / Linux
|
|
32
|
+
# .venv\Scripts\activate # Windows
|
|
33
|
+
|
|
34
|
+
# Install Python packages
|
|
35
|
+
pip install -r requirements.txt
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 2 — Install Playwright browsers
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
playwright install chromium
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> Only Chromium is needed. Run `playwright install` if you also want
|
|
45
|
+
> Firefox and WebKit.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 3 — Run the tool
|
|
50
|
+
|
|
51
|
+
### Create + launch N profiles and navigate each to Sora
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python main.py --profiles 3
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This will:
|
|
58
|
+
1. Create `profiles/profile_1`, `profiles/profile_2`, `profiles/profile_3`
|
|
59
|
+
2. Open three Chromium windows simultaneously, each with its own profile
|
|
60
|
+
3. Navigate each window to `https://sora.com`
|
|
61
|
+
|
|
62
|
+
Log in to **Google** and **Sora** manually in each window.
|
|
63
|
+
Your session (cookies, tokens) is saved automatically inside the profile folder.
|
|
64
|
+
Close all windows when done — next time you run the tool, you will still be logged in.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### Other usage examples
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Create profiles without opening a browser
|
|
72
|
+
python main.py --profiles 3 --no-launch
|
|
73
|
+
|
|
74
|
+
# Open browsers but skip auto-navigation to Sora
|
|
75
|
+
python main.py --profiles 3 --no-sora
|
|
76
|
+
|
|
77
|
+
# Use the default (1 profile)
|
|
78
|
+
python main.py
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 4 — Adding automation later
|
|
84
|
+
|
|
85
|
+
1. Open `sora_bot.py` and implement the stub functions (`open_sora`,
|
|
86
|
+
`upload_image`, `submit_prompt`, `create_video`).
|
|
87
|
+
2. Use `data_provider.py` → `get_next_job()` to feed prompts and images.
|
|
88
|
+
3. Call your bot from `main.py` after the browser context is created.
|
|
89
|
+
|
|
90
|
+
Each profile's `BrowserContext` is already authenticated — no extra login
|
|
91
|
+
logic is required once you have logged in manually.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
Edit `config.py` to change:
|
|
98
|
+
|
|
99
|
+
| Setting | Default | Description |
|
|
100
|
+
|-----------------------|-----------------|------------------------------------------|
|
|
101
|
+
| `PROFILES_DIR` | `./profiles` | Where profile folders are stored |
|
|
102
|
+
| `DEFAULT_PROFILE_COUNT` | `1` | Profiles used when `--profiles` is omitted |
|
|
103
|
+
| `SORA_URL` | `https://sora.com` | URL opened on launch |
|
|
104
|
+
| `BROWSER_HEADLESS` | `False` | Set `True` for headless / CI runs |
|
|
105
|
+
| `BROWSER_CHANNEL` | `"chromium"` | `"chrome"` to use system Chrome |
|
|
106
|
+
| `BROWSER_SLOW_MO` | `0` | ms delay between actions (for debugging) |
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Giao tiep voi backend API.
|
|
3
|
+
|
|
4
|
+
GET /products/pending?username=... — lay danh sach san pham can xu ly
|
|
5
|
+
PATCH /products/:id/done — danh dau san pham hoan thanh
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import urllib.error
|
|
11
|
+
import urllib.request
|
|
12
|
+
|
|
13
|
+
from .constants import API_BASE_URL
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_pending_products(username: str) -> list[dict]:
|
|
17
|
+
"""Lay danh sach san pham co status = not_start cua user."""
|
|
18
|
+
url = f"{API_BASE_URL}/products/pending?username={username}"
|
|
19
|
+
req = urllib.request.Request(url, headers={"Accept": "application/json"})
|
|
20
|
+
try:
|
|
21
|
+
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
22
|
+
return json.loads(resp.read().decode())
|
|
23
|
+
except urllib.error.HTTPError as e:
|
|
24
|
+
raise RuntimeError(f"Loi API ({e.code}): {e.reason}") from e
|
|
25
|
+
except Exception as e:
|
|
26
|
+
raise RuntimeError(f"Khong the ket noi API: {e}") from e
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def mark_product_done(product_id: int, backend_key: str) -> dict:
|
|
30
|
+
"""Danh dau san pham la done sau khi xu ly xong."""
|
|
31
|
+
url = f"{API_BASE_URL}/products/{product_id}/done"
|
|
32
|
+
req = urllib.request.Request(
|
|
33
|
+
url,
|
|
34
|
+
data=b"",
|
|
35
|
+
method="PATCH",
|
|
36
|
+
headers={
|
|
37
|
+
"x-backend-key": backend_key,
|
|
38
|
+
"Content-Length": "0",
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
try:
|
|
42
|
+
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
43
|
+
return json.loads(resp.read().decode())
|
|
44
|
+
except urllib.error.HTTPError as e:
|
|
45
|
+
if e.code == 401:
|
|
46
|
+
raise RuntimeError("Backend key khong hop le (401 Unauthorized).") from e
|
|
47
|
+
raise RuntimeError(f"Loi API mark done ({e.code}): {e.reason}") from e
|
|
48
|
+
except Exception as e:
|
|
49
|
+
raise RuntimeError(f"Khong the ket noi API: {e}") from e
|
|
Binary file
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quan ly vong doi Chrome.
|
|
3
|
+
|
|
4
|
+
LOGIN MODE : Mo Chrome binh thuong bang subprocess (khong co Playwright).
|
|
5
|
+
AUTOMATE MODE: Mo Chrome bang subprocess voi --remote-debugging-port,
|
|
6
|
+
sau do Playwright ket noi qua CDP.
|
|
7
|
+
=> Khong co banner "controlled by automated software".
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import socket
|
|
12
|
+
import subprocess
|
|
13
|
+
import time
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from .constants import (
|
|
17
|
+
CHROME_BINARY,
|
|
18
|
+
CDP_PORT_BASE,
|
|
19
|
+
CHROME_LAUNCH_TIMEOUT_SEC,
|
|
20
|
+
BROWSER_SLOW_MO,
|
|
21
|
+
SORA_URL,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Internal helpers
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
def _wait_for_cdp(port: int, proc: subprocess.Popen, timeout: float = 30.0) -> None:
|
|
30
|
+
"""Block cho den khi Chrome mo cong CDP, kiem tra process con song."""
|
|
31
|
+
deadline = time.time() + timeout
|
|
32
|
+
while time.time() < deadline:
|
|
33
|
+
# Neu Chrome thoat som thi bao loi ngay
|
|
34
|
+
ret = proc.poll()
|
|
35
|
+
if ret is not None:
|
|
36
|
+
stderr_out = ""
|
|
37
|
+
if proc.stderr:
|
|
38
|
+
stderr_out = proc.stderr.read().decode(errors="replace")[:500]
|
|
39
|
+
raise RuntimeError(
|
|
40
|
+
f"Chrome thoat ngay lap tuc (exit code {ret}).\n"
|
|
41
|
+
f"Stderr: {stderr_out or '(trong)'}"
|
|
42
|
+
)
|
|
43
|
+
try:
|
|
44
|
+
with socket.create_connection(("127.0.0.1", port), timeout=1):
|
|
45
|
+
return
|
|
46
|
+
except OSError:
|
|
47
|
+
time.sleep(0.4)
|
|
48
|
+
# Het timeout — lay stderr de debug
|
|
49
|
+
stderr_out = ""
|
|
50
|
+
if proc.stderr:
|
|
51
|
+
stderr_out = proc.stderr.read().decode(errors="replace")[:500]
|
|
52
|
+
raise TimeoutError(
|
|
53
|
+
f"Chrome khong mo cong CDP {port} sau {timeout}s.\n"
|
|
54
|
+
f"Stderr: {stderr_out or '(trong)'}\n"
|
|
55
|
+
"Thu tat tat ca Chrome dang mo roi chay lai."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _spawn_chrome(
|
|
60
|
+
profile_path: Path,
|
|
61
|
+
*,
|
|
62
|
+
debug_port: int | None = None,
|
|
63
|
+
url: str | None = None,
|
|
64
|
+
) -> subprocess.Popen:
|
|
65
|
+
cmd = [
|
|
66
|
+
CHROME_BINARY,
|
|
67
|
+
f"--user-data-dir={profile_path}",
|
|
68
|
+
"--no-first-run",
|
|
69
|
+
"--no-default-browser-check",
|
|
70
|
+
"--mute-audio",
|
|
71
|
+
"--disable-features=OptimizationGuideModelDownloading",
|
|
72
|
+
]
|
|
73
|
+
if debug_port:
|
|
74
|
+
cmd.append(f"--remote-debugging-port={debug_port}")
|
|
75
|
+
if url:
|
|
76
|
+
cmd.append(url)
|
|
77
|
+
print(f" [chrome] CMD: {' '.join(cmd[:4])} ...")
|
|
78
|
+
return subprocess.Popen(
|
|
79
|
+
cmd,
|
|
80
|
+
stdout=subprocess.DEVNULL,
|
|
81
|
+
stderr=subprocess.PIPE, # capture de debug
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
# Login mode
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
def launch_chrome_for_login(profile_path: Path, url: str | None = None) -> subprocess.Popen:
|
|
90
|
+
"""Mo mot cua so Chrome thong thuong de user dang nhap thu cong."""
|
|
91
|
+
print(f" [chrome] Mo profile: {profile_path.name} ...")
|
|
92
|
+
return _spawn_chrome(profile_path, url=url)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# Automate mode
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
def _remove_singleton_lock(profile_path: Path) -> None:
|
|
100
|
+
"""Xoa file khoa con lai neu Chrome bi tat dot ngot lan truoc."""
|
|
101
|
+
lock = profile_path / "SingletonLock"
|
|
102
|
+
if lock.exists():
|
|
103
|
+
lock.unlink()
|
|
104
|
+
print(f" [chrome] Da xoa SingletonLock cu: {lock}")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def launch_chrome_for_automation(
|
|
108
|
+
profile_path: Path,
|
|
109
|
+
debug_port: int,
|
|
110
|
+
timeout: float = 30.0,
|
|
111
|
+
) -> tuple:
|
|
112
|
+
"""
|
|
113
|
+
Mo Chrome voi cong CDP va ket noi Playwright qua CDP.
|
|
114
|
+
Tra ve (proc, playwright, browser, context, page).
|
|
115
|
+
Caller phai goi pw.stop() va close_chrome(proc) khi xong.
|
|
116
|
+
"""
|
|
117
|
+
from playwright.sync_api import sync_playwright
|
|
118
|
+
|
|
119
|
+
profile_name = profile_path.name
|
|
120
|
+
_remove_singleton_lock(profile_path)
|
|
121
|
+
|
|
122
|
+
print(f" [chrome] Khoi dong: {profile_name} (port {debug_port}) ...")
|
|
123
|
+
proc = _spawn_chrome(profile_path, debug_port=debug_port, url=SORA_URL)
|
|
124
|
+
|
|
125
|
+
print(f" [chrome] Cho CDP san sang (port {debug_port}) ...")
|
|
126
|
+
_wait_for_cdp(debug_port, proc=proc, timeout=timeout)
|
|
127
|
+
|
|
128
|
+
print(f" [chrome] Ket noi Playwright ...")
|
|
129
|
+
pw = sync_playwright().start()
|
|
130
|
+
browser = pw.chromium.connect_over_cdp(
|
|
131
|
+
f"http://127.0.0.1:{debug_port}",
|
|
132
|
+
slow_mo=BROWSER_SLOW_MO,
|
|
133
|
+
)
|
|
134
|
+
context = browser.contexts[0] if browser.contexts else browser.new_context()
|
|
135
|
+
page = context.pages[0] if context.pages else context.new_page()
|
|
136
|
+
|
|
137
|
+
print(f" [chrome] San sang: {profile_name}")
|
|
138
|
+
return proc, pw, browser, context, page
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def close_chrome(proc: subprocess.Popen) -> None:
|
|
142
|
+
"""Tat Chrome mot cach an toan."""
|
|
143
|
+
proc.terminate()
|
|
144
|
+
try:
|
|
145
|
+
proc.wait(timeout=5)
|
|
146
|
+
except subprocess.TimeoutExpired:
|
|
147
|
+
proc.kill()
|