dy-cli 0.2.0__py3-none-any.whl
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.
- dy_cli/__init__.py +3 -0
- dy_cli/commands/__init__.py +0 -0
- dy_cli/commands/account.py +103 -0
- dy_cli/commands/analytics.py +120 -0
- dy_cli/commands/auth.py +159 -0
- dy_cli/commands/config_cmd.py +67 -0
- dy_cli/commands/download.py +212 -0
- dy_cli/commands/init.py +200 -0
- dy_cli/commands/interact.py +140 -0
- dy_cli/commands/live.py +141 -0
- dy_cli/commands/profile.py +78 -0
- dy_cli/commands/publish.py +123 -0
- dy_cli/commands/search.py +131 -0
- dy_cli/commands/trending.py +82 -0
- dy_cli/engines/__init__.py +0 -0
- dy_cli/engines/api_client.py +665 -0
- dy_cli/engines/playwright_client.py +836 -0
- dy_cli/main.py +144 -0
- dy_cli/utils/__init__.py +0 -0
- dy_cli/utils/config.py +99 -0
- dy_cli/utils/envelope.py +49 -0
- dy_cli/utils/export.py +68 -0
- dy_cli/utils/index_cache.py +83 -0
- dy_cli/utils/output.py +283 -0
- dy_cli/utils/signature.py +183 -0
- dy_cli-0.2.0.dist-info/METADATA +376 -0
- dy_cli-0.2.0.dist-info/RECORD +34 -0
- dy_cli-0.2.0.dist-info/WHEEL +4 -0
- dy_cli-0.2.0.dist-info/entry_points.txt +2 -0
- dy_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
- scripts/chrome_launcher.py +71 -0
- scripts/douyin_analytics.py +99 -0
- scripts/douyin_login.py +64 -0
- scripts/douyin_publisher.py +199 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dy-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: 抖音命令行工具 — 发布、搜索、下载、互动、数据分析、热榜、直播
|
|
5
|
+
Project-URL: Homepage, https://github.com/Youhai020616/douyin
|
|
6
|
+
Project-URL: Repository, https://github.com/Youhai020616/douyin
|
|
7
|
+
Project-URL: Issues, https://github.com/Youhai020616/douyin/issues
|
|
8
|
+
Author-email: Youhai <youhai020616@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: automation,cli,douyin,dy,tiktok,抖音
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: browser-cookie3>=0.19
|
|
19
|
+
Requires-Dist: click>=8.0
|
|
20
|
+
Requires-Dist: httpx>=0.27.0
|
|
21
|
+
Requires-Dist: playwright>=1.40
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
Requires-Dist: rich>=13.0
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<h1 align="center">🎬 douyin</h1>
|
|
28
|
+
<p align="center">AI-powered Douyin (抖音/TikTok China) automation — search, download, publish, engage, and analyze.</p>
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
<p align="center">
|
|
32
|
+
<a href="#cli-quick-start">CLI Quick Start</a> •
|
|
33
|
+
<a href="#features">Features</a> •
|
|
34
|
+
<a href="#commands">Commands</a> •
|
|
35
|
+
<a href="#scripts">Scripts</a> •
|
|
36
|
+
<a href="#claude-code-integration">Claude Code</a> •
|
|
37
|
+
<a href="./LICENSE">License</a>
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
<p align="center">
|
|
43
|
+
<img src="./demo.gif" alt="dy-cli demo" width="800">
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
## CLI Quick Start
|
|
47
|
+
|
|
48
|
+
The fastest way to get started — **3 commands** from zero to searching:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# 1. Clone
|
|
52
|
+
git clone https://github.com/your-username/douyin.git
|
|
53
|
+
cd douyin
|
|
54
|
+
|
|
55
|
+
# 2. One-click install (auto: Python check → venv → pip install → Playwright Chromium)
|
|
56
|
+
bash setup.sh
|
|
57
|
+
|
|
58
|
+
# 3. Initialize (auto: install Chromium → config → QR login)
|
|
59
|
+
source activate.sh && dy init
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then just use:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
dy search "AI创业" # Search
|
|
66
|
+
dy trending # Hot trending
|
|
67
|
+
dy download https://v.douyin.com/xxxxx/ # Download (no watermark)
|
|
68
|
+
dy publish -t "Hello" -c "My first post" -v video.mp4 # Publish
|
|
69
|
+
dy live info ROOM_ID # Live stream info
|
|
70
|
+
dy analytics # Dashboard
|
|
71
|
+
dy --help # All commands
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> 📖 Full CLI guide: [docs/cli-guide.md](docs/cli-guide.md)
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## What is this?
|
|
79
|
+
|
|
80
|
+
A complete toolkit for automating Douyin (抖音/TikTok China) operations through two complementary engines:
|
|
81
|
+
|
|
82
|
+
| Engine | Technology | Use Cases | Startup |
|
|
83
|
+
|--------|-----------|-----------|---------|
|
|
84
|
+
| **API Client** | Python httpx, reverse-engineered API | Search, download, comments, trending, live | Instant |
|
|
85
|
+
| **Playwright** | Python Playwright, browser automation | Publish, login, analytics dashboard | On-demand |
|
|
86
|
+
|
|
87
|
+
Built as an [OpenClaw](https://github.com/openclaw/openclaw) Skill, but works standalone or with any MCP-compatible client (Claude Code, Cursor, etc.).
|
|
88
|
+
|
|
89
|
+
## Features
|
|
90
|
+
|
|
91
|
+
- 🔍 **Search** — Keyword search with filters (sort, time, type)
|
|
92
|
+
- 📥 **Download** — No-watermark video/image download with progress bar
|
|
93
|
+
- 📝 **Publish** — Video and image posts with tags, scheduling, visibility
|
|
94
|
+
- 🔥 **Trending** — Real-time hot search rankings with watch mode
|
|
95
|
+
- 📺 **Live** — Live stream info, stream URL extraction, ffmpeg recording
|
|
96
|
+
- 💬 **Engage** — Comment, like, favorite, follow
|
|
97
|
+
- 📊 **Analytics** — Creator dashboard data export (CSV)
|
|
98
|
+
- 🔔 **Notifications** — Fetch interaction notifications
|
|
99
|
+
- 👤 **Profile** — Fetch any user's profile and posts
|
|
100
|
+
- 👥 **Multi-Account** — Isolated cookie storage per account
|
|
101
|
+
- 🔐 **QR Code Login** — Scan-to-login via Playwright, persistent cookie storage
|
|
102
|
+
|
|
103
|
+
## Quick Start
|
|
104
|
+
|
|
105
|
+
### Prerequisites
|
|
106
|
+
|
|
107
|
+
- Python 3.10+
|
|
108
|
+
- Playwright Chromium (auto-installed by `setup.sh`)
|
|
109
|
+
- ffmpeg (optional, for live recording: `brew install ffmpeg`)
|
|
110
|
+
|
|
111
|
+
### 1. Clone & Install
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
git clone https://github.com/your-username/douyin.git
|
|
115
|
+
cd douyin
|
|
116
|
+
bash setup.sh
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 2. Initialize & Login
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
source activate.sh
|
|
123
|
+
dy init
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This will:
|
|
127
|
+
1. Check your environment
|
|
128
|
+
2. Install Playwright Chromium
|
|
129
|
+
3. Configure proxy (optional)
|
|
130
|
+
4. Open browser for QR code login
|
|
131
|
+
|
|
132
|
+
### 3. Start Using
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
dy search "旅行" # Search
|
|
136
|
+
dy trending # Hot topics
|
|
137
|
+
dy download URL # Download video
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Commands
|
|
143
|
+
|
|
144
|
+
### Search & Discovery
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
dy search "关键词" # Basic search
|
|
148
|
+
dy search "咖啡" --sort 最多点赞 # Sort by likes
|
|
149
|
+
dy search "春招" --time 一天内 --type video # Filters
|
|
150
|
+
dy trending # Hot trending list
|
|
151
|
+
dy trending --watch # Auto-refresh every 5 min
|
|
152
|
+
dy trending --json-output # JSON output
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Download
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
dy download https://v.douyin.com/xxxxx/ # Share link
|
|
159
|
+
dy download https://www.douyin.com/video/123 # Full link
|
|
160
|
+
dy download 1234567890 # Video ID
|
|
161
|
+
dy download URL --music # Also download BGM
|
|
162
|
+
dy download URL -o ~/Videos # Custom output dir
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Publish
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Video
|
|
169
|
+
dy publish -t "标题" -c "描述" -v video.mp4
|
|
170
|
+
|
|
171
|
+
# Image post
|
|
172
|
+
dy publish -t "标题" -c "描述" -i img1.jpg -i img2.jpg
|
|
173
|
+
|
|
174
|
+
# With tags
|
|
175
|
+
dy publish -t "旅行日记" -c "巴厘岛" -v trip.mp4 --tags 旅行 --tags 巴厘岛
|
|
176
|
+
|
|
177
|
+
# Private (test)
|
|
178
|
+
dy publish -t "测试" -c "测试" -v test.mp4 --visibility 仅自己可见
|
|
179
|
+
|
|
180
|
+
# Scheduled
|
|
181
|
+
dy publish -t "早安" -c "新的一天" -v morning.mp4 --schedule "2026-03-16T08:00:00+08:00"
|
|
182
|
+
|
|
183
|
+
# Preview only
|
|
184
|
+
dy publish -t "标题" -c "描述" -v video.mp4 --dry-run
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Video Detail & Comments
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
dy detail AWEME_ID # Video detail
|
|
191
|
+
dy detail AWEME_ID --comments # With comments
|
|
192
|
+
dy comments AWEME_ID # Comments only
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Interaction
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
dy like AWEME_ID # Like
|
|
199
|
+
dy favorite AWEME_ID # Favorite
|
|
200
|
+
dy comment AWEME_ID -c "Great!" # Comment
|
|
201
|
+
dy follow SEC_USER_ID # Follow user
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Live Stream
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
dy live info ROOM_ID # Live info + stream URLs
|
|
208
|
+
dy live record ROOM_ID # Record with ffmpeg
|
|
209
|
+
dy live record ROOM_ID --quality HD1 # Specific quality
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Analytics & Notifications
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
dy analytics # Creator dashboard
|
|
216
|
+
dy analytics --csv data.csv # Export CSV
|
|
217
|
+
dy notifications # Messages
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### User Profile
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
dy me # My info
|
|
224
|
+
dy profile SEC_USER_ID # User profile
|
|
225
|
+
dy profile SEC_USER_ID --posts # With post list
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Multi-Account
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
dy account list # List accounts
|
|
232
|
+
dy account add work # Add & login
|
|
233
|
+
dy account default work # Set default
|
|
234
|
+
dy account remove work # Remove
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Configuration
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
dy config show # Show config
|
|
241
|
+
dy config set api.proxy http://127.0.0.1:7897
|
|
242
|
+
dy config set default.download_dir ~/Videos
|
|
243
|
+
dy config reset # Reset to defaults
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Command Aliases
|
|
247
|
+
|
|
248
|
+
| Alias | Full Command |
|
|
249
|
+
|-------|-------------|
|
|
250
|
+
| `dy pub` | `dy publish` |
|
|
251
|
+
| `dy s` | `dy search` |
|
|
252
|
+
| `dy dl` | `dy download` |
|
|
253
|
+
| `dy t` | `dy trending` |
|
|
254
|
+
| `dy fav` | `dy favorite` |
|
|
255
|
+
| `dy noti` | `dy notifications` |
|
|
256
|
+
| `dy stat` | `dy status` |
|
|
257
|
+
| `dy acc` | `dy account` |
|
|
258
|
+
| `dy cfg` | `dy config` |
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Engine Architecture
|
|
263
|
+
|
|
264
|
+
| Engine | Features | Technology |
|
|
265
|
+
|--------|----------|------------|
|
|
266
|
+
| **API Client** | Search, download, comments, trending, live, user profile | httpx + reverse-engineered API |
|
|
267
|
+
| **Playwright** | Publish, login, analytics, notifications | Playwright browser automation |
|
|
268
|
+
|
|
269
|
+
Most commands auto-select the best engine. Only `publish`, `analytics`, and `login` require Playwright.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Scripts
|
|
274
|
+
|
|
275
|
+
Standalone Python scripts for direct use without the CLI:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
# Login
|
|
279
|
+
python scripts/douyin_login.py --account default
|
|
280
|
+
|
|
281
|
+
# Publish video
|
|
282
|
+
python scripts/douyin_publisher.py -t "标题" -c "描述" -v video.mp4
|
|
283
|
+
|
|
284
|
+
# Publish images
|
|
285
|
+
python scripts/douyin_publisher.py -t "标题" -c "描述" -i img1.jpg img2.jpg
|
|
286
|
+
|
|
287
|
+
# Analytics
|
|
288
|
+
python scripts/douyin_analytics.py --csv output.csv
|
|
289
|
+
|
|
290
|
+
# Chrome management
|
|
291
|
+
python scripts/chrome_launcher.py
|
|
292
|
+
python scripts/chrome_launcher.py --kill
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Claude Code Integration
|
|
298
|
+
|
|
299
|
+
See [docs/claude-code-integration.md](docs/claude-code-integration.md) for setup instructions.
|
|
300
|
+
|
|
301
|
+
## Project Structure
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
douyin/
|
|
305
|
+
├── README.md # This file
|
|
306
|
+
├── SKILL.md # OpenClaw skill definition
|
|
307
|
+
├── pyproject.toml # CLI package config
|
|
308
|
+
├── manifest.json # Skill metadata
|
|
309
|
+
├── LICENSE # MIT License
|
|
310
|
+
├── requirements.txt # Python dependencies
|
|
311
|
+
├── setup.sh # One-click install (macOS/Linux)
|
|
312
|
+
├── setup.bat # One-click install (Windows)
|
|
313
|
+
├── activate.sh # Environment activation
|
|
314
|
+
├── src/dy_cli/ # ⭐ CLI package
|
|
315
|
+
│ ├── main.py # Unified entry point (dy command)
|
|
316
|
+
│ ├── engines/
|
|
317
|
+
│ │ ├── api_client.py # Reverse-engineered API client
|
|
318
|
+
│ │ └── playwright_client.py # Playwright browser automation
|
|
319
|
+
│ ├── commands/
|
|
320
|
+
│ │ ├── init.py # dy init (guided setup)
|
|
321
|
+
│ │ ├── auth.py # dy login/logout/status
|
|
322
|
+
│ │ ├── publish.py # dy publish (Playwright)
|
|
323
|
+
│ │ ├── search.py # dy search/detail (API)
|
|
324
|
+
│ │ ├── download.py # dy download (API) ⭐
|
|
325
|
+
│ │ ├── interact.py # dy like/comment/favorite/follow
|
|
326
|
+
│ │ ├── trending.py # dy trending (API) ⭐
|
|
327
|
+
│ │ ├── live.py # dy live info/record ⭐
|
|
328
|
+
│ │ ├── analytics.py # dy analytics (Playwright)
|
|
329
|
+
│ │ ├── profile.py # dy me/profile
|
|
330
|
+
│ │ ├── account.py # dy account management
|
|
331
|
+
│ │ └── config_cmd.py # dy config
|
|
332
|
+
│ └── utils/
|
|
333
|
+
│ ├── config.py # ~/.dy/config.json management
|
|
334
|
+
│ ├── output.py # Rich formatted output
|
|
335
|
+
│ └── signature.py # Douyin signature utilities
|
|
336
|
+
├── scripts/
|
|
337
|
+
│ ├── douyin_login.py # Login script
|
|
338
|
+
│ ├── douyin_publisher.py # Publish script
|
|
339
|
+
│ ├── douyin_analytics.py # Analytics script
|
|
340
|
+
│ └── chrome_launcher.py # Chrome lifecycle
|
|
341
|
+
├── config/
|
|
342
|
+
│ └── accounts.json.example
|
|
343
|
+
└── docs/
|
|
344
|
+
├── cli-guide.md # CLI usage guide
|
|
345
|
+
└── claude-code-integration.md # Claude Code setup
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Platform Support
|
|
349
|
+
|
|
350
|
+
| Component | macOS | Linux | Windows |
|
|
351
|
+
|-----------|:-----:|:-----:|:-------:|
|
|
352
|
+
| **dy CLI** | ✅ | ✅ | ✅ |
|
|
353
|
+
| API Client | ✅ | ✅ | ✅ |
|
|
354
|
+
| Playwright | ✅ | ✅ | ✅ |
|
|
355
|
+
|
|
356
|
+
## Tips & Known Issues
|
|
357
|
+
|
|
358
|
+
- **Signature algorithm**: Douyin frequently updates `a-bogus`/`x-bogus` — some API calls may need browser-based signing
|
|
359
|
+
- **Login**: Cookie expires periodically, re-login with `dy login`
|
|
360
|
+
- **Rate limiting**: Avoid rapid-fire requests, add delays between batch operations
|
|
361
|
+
- **Proxy**: Outside China may need proxy: `dy config set api.proxy http://...`
|
|
362
|
+
- **Live recording**: Requires ffmpeg: `brew install ffmpeg` (macOS)
|
|
363
|
+
|
|
364
|
+
## Contributing
|
|
365
|
+
|
|
366
|
+
Issues and PRs welcome! Areas where help is needed:
|
|
367
|
+
|
|
368
|
+
- [ ] Improved `a-bogus` signature algorithm
|
|
369
|
+
- [ ] Batch download by user/hashtag
|
|
370
|
+
- [ ] Playwright interaction (like/comment/follow)
|
|
371
|
+
- [ ] More analytics data points
|
|
372
|
+
- [ ] Test suite
|
|
373
|
+
|
|
374
|
+
## License
|
|
375
|
+
|
|
376
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
scripts/chrome_launcher.py,sha256=wOsxSyoFx4XtFe7ZHK3O0RY9xlbf5U5OrbjYf4YaZhI,2269
|
|
2
|
+
scripts/douyin_analytics.py,sha256=iIom8IeHOz9KhZMOMWr4C1aOzeR4191XHt7Vb8jqrEw,3450
|
|
3
|
+
scripts/douyin_login.py,sha256=UTwK4-YiOCX6pXIck49PV9I6hTZv84Dy6G9evaDgeRI,1933
|
|
4
|
+
scripts/douyin_publisher.py,sha256=Ny2CVGxJnPcJ04dZWia_Mi5nuVDotTwHtMHOSmZYAWE,7033
|
|
5
|
+
dy_cli/__init__.py,sha256=KmtSQreEp4d8uycTz7chUSNz0tyEO6N0YRc94R6NAoU,65
|
|
6
|
+
dy_cli/main.py,sha256=sKXUUW_P_Kw4juTO7_019J-yACgtWVSFCdcgg7wFtHc,4212
|
|
7
|
+
dy_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
dy_cli/commands/account.py,sha256=Ahud5A2AaVkQ8NYoFSMEAeZWzzKP9uWAVnkj4zvMM54,3199
|
|
9
|
+
dy_cli/commands/analytics.py,sha256=HTrJHi7BfM9aHgq_kPzci3samFCZmAyh3XQEbLJT58g,3568
|
|
10
|
+
dy_cli/commands/auth.py,sha256=PGU43AC50wv6jOW9FGeFe149o8yNp2Tdv2NLaVxFj7U,5417
|
|
11
|
+
dy_cli/commands/config_cmd.py,sha256=5JFuUBCl4_8YAn7io85K6eFHSIOxEjy_Dl2YbbVgwDk,1752
|
|
12
|
+
dy_cli/commands/download.py,sha256=XMOt5KXHmZOeb0wloLVDIS6WEhqLjhWI1AtOy8b7fRU,7683
|
|
13
|
+
dy_cli/commands/init.py,sha256=SxVmG36TJ1fyhY1MHzEz1a4r96ffJ7Fsbpsj8_GgicQ,7555
|
|
14
|
+
dy_cli/commands/interact.py,sha256=X6I6wVlnvkeZ1EQ1zhyac-q2CjfrHZWR_ZAndTcpBk0,4881
|
|
15
|
+
dy_cli/commands/live.py,sha256=xbtF4A6Qg39YpNVhXP414NgH-G41K8d3RIkvKTNYbgk,4726
|
|
16
|
+
dy_cli/commands/profile.py,sha256=1cwgrYGBeF5iJRRceyzGkPZ568QTzox7ZROxGkAAmeg,2650
|
|
17
|
+
dy_cli/commands/publish.py,sha256=1UyxsTei4pROsaOP2hmgfbDb7Fucld2eiHwL18weEU4,4635
|
|
18
|
+
dy_cli/commands/search.py,sha256=UEzU1lgwm5i8cT4-N7CXgvxZShJtC6-nhNNUmgacDqk,4275
|
|
19
|
+
dy_cli/commands/trending.py,sha256=iUOs0JKffsXv4xcmA2Bxgp37HcXPEy4SpwcmuOVcDm4,2714
|
|
20
|
+
dy_cli/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
dy_cli/engines/api_client.py,sha256=AksJ-4jL_qYaUP5SyQWW3cLleDVccIOz41x81Ze4xO8,23211
|
|
22
|
+
dy_cli/engines/playwright_client.py,sha256=-p3DW_y1wXn0JxrHc2L-jgCwdRL_WmYRMofztofsw0Q,35198
|
|
23
|
+
dy_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
dy_cli/utils/config.py,sha256=S73PvZvMWGZPJf3VYV3MnK-HwMXBMFWemRz7gU71BRA,2781
|
|
25
|
+
dy_cli/utils/envelope.py,sha256=cJj9LPhCwSfPr-Jxkvrktk02SSL3SOL898cSQCCidmA,1434
|
|
26
|
+
dy_cli/utils/export.py,sha256=I3aG7Li5y1K3MwZKmhdzUS09THRk-IFlnnGEwWJ0rDI,2034
|
|
27
|
+
dy_cli/utils/index_cache.py,sha256=o1uObcXCE1QHCeZ6Odfl4ch9GEfMt53rud4bVzM27uc,2815
|
|
28
|
+
dy_cli/utils/output.py,sha256=loqW4dc9JvEBWuLZBzabOSxK3yM2u2b46Y4H0fec3Pc,9715
|
|
29
|
+
dy_cli/utils/signature.py,sha256=LM4V_tieB-lHo5zmpIRlHKDBqcf_yS2_16OT1OR6nKo,5870
|
|
30
|
+
dy_cli-0.2.0.dist-info/METADATA,sha256=b-L_5nOb8FLJYocUlMm8r6OCvGfFA-UxTnVN7xk3eGk,12358
|
|
31
|
+
dy_cli-0.2.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
32
|
+
dy_cli-0.2.0.dist-info/entry_points.txt,sha256=FV1C9hRIdlkSnnNwvU515738x5AJxaFxFxFduCcjbAM,39
|
|
33
|
+
dy_cli-0.2.0.dist-info/licenses/LICENSE,sha256=ESYyLizI0WWtxMeS7rGVcX3ivMezm-HOd5WdeOh-9oU,1056
|
|
34
|
+
dy_cli-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Chrome/Chromium 生命周期管理。
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python scripts/chrome_launcher.py # 启动 (Playwright Chromium)
|
|
6
|
+
python scripts/chrome_launcher.py --headless # 无头模式
|
|
7
|
+
python scripts/chrome_launcher.py --kill # 关闭
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import asyncio
|
|
13
|
+
import os
|
|
14
|
+
import signal
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def launch_chromium(headless: bool = False):
|
|
20
|
+
"""使用 Playwright 启动 Chromium。"""
|
|
21
|
+
from playwright.async_api import async_playwright
|
|
22
|
+
|
|
23
|
+
async with async_playwright() as pw:
|
|
24
|
+
browser = await pw.chromium.launch(headless=headless)
|
|
25
|
+
context = await browser.new_context()
|
|
26
|
+
page = await context.new_page()
|
|
27
|
+
await page.goto("https://www.douyin.com/", wait_until="domcontentloaded")
|
|
28
|
+
|
|
29
|
+
endpoint = browser._impl_obj._browser.ws_endpoint if hasattr(browser._impl_obj, '_browser') else "N/A"
|
|
30
|
+
print(f"[dy] Chromium 已启动")
|
|
31
|
+
print(f"[dy] Headless: {headless}")
|
|
32
|
+
print(f"[dy] 按 Ctrl+C 关闭")
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
while True:
|
|
36
|
+
await asyncio.sleep(1)
|
|
37
|
+
except (KeyboardInterrupt, asyncio.CancelledError):
|
|
38
|
+
pass
|
|
39
|
+
finally:
|
|
40
|
+
await browser.close()
|
|
41
|
+
print("[dy] Chromium 已关闭")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def kill_chromium():
|
|
45
|
+
"""关闭所有 Playwright Chromium 进程。"""
|
|
46
|
+
try:
|
|
47
|
+
if sys.platform == "darwin":
|
|
48
|
+
subprocess.run(["pkill", "-f", "chromium"], capture_output=True)
|
|
49
|
+
elif sys.platform == "win32":
|
|
50
|
+
subprocess.run(["taskkill", "/F", "/IM", "chromium.exe"], capture_output=True)
|
|
51
|
+
else:
|
|
52
|
+
subprocess.run(["pkill", "-f", "chromium"], capture_output=True)
|
|
53
|
+
print("[dy] Chromium 进程已终止")
|
|
54
|
+
except Exception as e:
|
|
55
|
+
print(f"[dy] 关闭失败: {e}")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def main():
|
|
59
|
+
parser = argparse.ArgumentParser(description="Chrome/Chromium 管理")
|
|
60
|
+
parser.add_argument("--headless", action="store_true", help="无头模式")
|
|
61
|
+
parser.add_argument("--kill", action="store_true", help="关闭 Chromium")
|
|
62
|
+
args = parser.parse_args()
|
|
63
|
+
|
|
64
|
+
if args.kill:
|
|
65
|
+
kill_chromium()
|
|
66
|
+
else:
|
|
67
|
+
asyncio.run(launch_chromium(headless=args.headless))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
main()
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
抖音数据看板脚本 — Playwright 爬取 creator.douyin.com 数据。
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python scripts/douyin_analytics.py
|
|
6
|
+
python scripts/douyin_analytics.py --csv output.csv
|
|
7
|
+
python scripts/douyin_analytics.py --account work
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import asyncio
|
|
13
|
+
import csv
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def get_analytics(account: str = "default", csv_file: str | None = None):
|
|
20
|
+
"""获取创作者数据看板。"""
|
|
21
|
+
from playwright.async_api import async_playwright
|
|
22
|
+
|
|
23
|
+
cookie_file = os.path.expanduser(f"~/.dy/cookies/{account}.json")
|
|
24
|
+
if not os.path.isfile(cookie_file):
|
|
25
|
+
print(f"[dy] Cookie 文件不存在: {cookie_file}")
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
async with async_playwright() as pw:
|
|
29
|
+
browser = await pw.chromium.launch(headless=True)
|
|
30
|
+
context = await browser.new_context(storage_state=cookie_file)
|
|
31
|
+
page = await context.new_page()
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
print("[dy] 正在访问数据看板...")
|
|
35
|
+
await page.goto(
|
|
36
|
+
"https://creator.douyin.com/creator-micro/data/stats/self-content",
|
|
37
|
+
wait_until="domcontentloaded",
|
|
38
|
+
)
|
|
39
|
+
await page.wait_for_timeout(5000)
|
|
40
|
+
|
|
41
|
+
if await page.get_by_text("扫码登录").count() > 0:
|
|
42
|
+
print("[dy] Cookie 已失效")
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
# Extract data
|
|
46
|
+
data = await page.evaluate("""() => {
|
|
47
|
+
const rows = [];
|
|
48
|
+
const items = document.querySelectorAll('tr, [class*="content-item"]');
|
|
49
|
+
items.forEach(item => {
|
|
50
|
+
const cells = item.querySelectorAll('td, [class*="cell"]');
|
|
51
|
+
if (cells.length >= 3) {
|
|
52
|
+
const texts = Array.from(cells).map(c => c.textContent.trim());
|
|
53
|
+
rows.push({
|
|
54
|
+
'标题': texts[0] || '-',
|
|
55
|
+
'发布时间': texts[1] || '-',
|
|
56
|
+
'播放': texts[2] || '-',
|
|
57
|
+
'完播率': texts[3] || '-',
|
|
58
|
+
'点赞': texts[4] || '-',
|
|
59
|
+
'评论': texts[5] || '-',
|
|
60
|
+
'分享': texts[6] || '-',
|
|
61
|
+
'涨粉': texts[7] || '-',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return rows;
|
|
66
|
+
}""")
|
|
67
|
+
|
|
68
|
+
print(f"[dy] 获取到 {len(data)} 条数据")
|
|
69
|
+
|
|
70
|
+
# Output JSON
|
|
71
|
+
print(json.dumps(data, ensure_ascii=False, indent=2))
|
|
72
|
+
|
|
73
|
+
# Export CSV
|
|
74
|
+
if csv_file and data:
|
|
75
|
+
keys = data[0].keys()
|
|
76
|
+
with open(csv_file, "w", newline="", encoding="utf-8-sig") as f:
|
|
77
|
+
writer = csv.DictWriter(f, fieldnames=keys)
|
|
78
|
+
writer.writeheader()
|
|
79
|
+
writer.writerows(data)
|
|
80
|
+
print(f"[dy] CSV 已导出: {csv_file}")
|
|
81
|
+
|
|
82
|
+
return data
|
|
83
|
+
|
|
84
|
+
finally:
|
|
85
|
+
await browser.close()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def main():
|
|
89
|
+
parser = argparse.ArgumentParser(description="抖音数据看板")
|
|
90
|
+
parser.add_argument("--account", default="default", help="账号名")
|
|
91
|
+
parser.add_argument("--csv", default=None, help="导出 CSV 文件路径")
|
|
92
|
+
args = parser.parse_args()
|
|
93
|
+
|
|
94
|
+
result = asyncio.run(get_analytics(args.account, args.csv))
|
|
95
|
+
sys.exit(0 if result is not None else 1)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
main()
|
scripts/douyin_login.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
抖音登录脚本 — Playwright 扫码登录,保存 Cookie。
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python scripts/douyin_login.py
|
|
6
|
+
python scripts/douyin_login.py --account work
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import asyncio
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def login(account: str = "default"):
|
|
17
|
+
"""打开浏览器扫码登录抖音。"""
|
|
18
|
+
from playwright.async_api import async_playwright
|
|
19
|
+
|
|
20
|
+
# Cookie save path
|
|
21
|
+
cookie_dir = os.path.expanduser("~/.dy/cookies")
|
|
22
|
+
os.makedirs(cookie_dir, exist_ok=True)
|
|
23
|
+
cookie_file = os.path.join(cookie_dir, f"{account}.json")
|
|
24
|
+
|
|
25
|
+
async with async_playwright() as pw:
|
|
26
|
+
browser = await pw.chromium.launch(headless=False)
|
|
27
|
+
context = await browser.new_context()
|
|
28
|
+
page = await context.new_page()
|
|
29
|
+
|
|
30
|
+
print("[dy] 正在打开抖音创作者中心...")
|
|
31
|
+
await page.goto("https://creator.douyin.com/", wait_until="domcontentloaded")
|
|
32
|
+
|
|
33
|
+
print("[dy] 请使用抖音 App 扫码登录")
|
|
34
|
+
print("[dy] 扫码后浏览器会自动关闭")
|
|
35
|
+
|
|
36
|
+
# Wait for login — detect navigation to creator dashboard
|
|
37
|
+
try:
|
|
38
|
+
await page.wait_for_url("**/creator-micro/**", timeout=120000)
|
|
39
|
+
await page.wait_for_timeout(3000)
|
|
40
|
+
print("[dy] 登录成功!")
|
|
41
|
+
except Exception:
|
|
42
|
+
print("[dy] 登录超时")
|
|
43
|
+
await browser.close()
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
# Save cookies (playwright storage_state format)
|
|
47
|
+
await context.storage_state(path=cookie_file)
|
|
48
|
+
print(f"[dy] Cookie 已保存: {cookie_file}")
|
|
49
|
+
|
|
50
|
+
await browser.close()
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def main():
|
|
55
|
+
parser = argparse.ArgumentParser(description="抖音扫码登录")
|
|
56
|
+
parser.add_argument("--account", default="default", help="账号名 (默认: default)")
|
|
57
|
+
args = parser.parse_args()
|
|
58
|
+
|
|
59
|
+
ok = asyncio.run(login(args.account))
|
|
60
|
+
sys.exit(0 if ok else 1)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
main()
|