rednote-cli 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.
- rednote_cli-0.1.0/PKG-INFO +81 -0
- rednote_cli-0.1.0/README.md +65 -0
- rednote_cli-0.1.0/pyproject.toml +34 -0
- rednote_cli-0.1.0/setup.cfg +4 -0
- rednote_cli-0.1.0/src/rednote_cli/__init__.py +5 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/__init__.py +0 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/common/__init__.py +0 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/common/app_utils.py +77 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/common/config.py +83 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/common/enums.py +17 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/common/errors.py +22 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/core/__init__.py +0 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/core/account_manager.py +349 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/core/browser/__init__.py +0 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/core/browser/manager.py +247 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/core/database/__init__.py +0 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/core/database/manager.py +334 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/platforms/__init__.py +0 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/platforms/base.py +62 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/platforms/factory.py +55 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/platforms/publishing/__init__.py +12 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/platforms/publishing/media.py +275 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/platforms/publishing/models.py +59 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/platforms/publishing/validator.py +124 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/services/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/_runtime/services/scraper_service.py +235 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/output/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/output/event_stream.py +29 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/output/formatter_json.py +23 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/output/formatter_table.py +39 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/output/writer.py +17 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/persistence/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/persistence/file_account_repo.py +51 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/platform/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/platform/rednote/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/platform/rednote/extractor.py +65 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/platform/rednote/publisher.py +26 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/platform/rednote/runtime_extractor.py +818 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/platform/rednote/runtime_publisher.py +373 -0
- rednote_cli-0.1.0/src/rednote_cli/adapters/platform/rednote/runtime_registration.py +20 -0
- rednote_cli-0.1.0/src/rednote_cli/application/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/application/dto/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/application/dto/input_models.py +121 -0
- rednote_cli-0.1.0/src/rednote_cli/application/dto/output_models.py +78 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/account_list.py +9 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/account_mutation.py +22 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/auth_login.py +64 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/auth_status.py +96 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/doctor.py +49 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/init_runtime.py +20 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/note_get.py +22 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/note_search.py +26 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/publish_note.py +25 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/user_get.py +18 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/user_search.py +8 -0
- rednote_cli-0.1.0/src/rednote_cli/application/use_cases/user_self.py +8 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/__main__.py +5 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/commands/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/commands/account.py +204 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/commands/doctor.py +20 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/commands/init.py +20 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/commands/note.py +101 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/commands/publish.py +147 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/commands/search.py +185 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/commands/user.py +113 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/main.py +163 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/options.py +13 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/runtime.py +142 -0
- rednote_cli-0.1.0/src/rednote_cli/cli/utils.py +74 -0
- rednote_cli-0.1.0/src/rednote_cli/domain/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/domain/errors.py +50 -0
- rednote_cli-0.1.0/src/rednote_cli/domain/note_search_filters.py +155 -0
- rednote_cli-0.1.0/src/rednote_cli/infra/__init__.py +1 -0
- rednote_cli-0.1.0/src/rednote_cli/infra/exit_codes.py +30 -0
- rednote_cli-0.1.0/src/rednote_cli/infra/logger.py +11 -0
- rednote_cli-0.1.0/src/rednote_cli/infra/paths.py +31 -0
- rednote_cli-0.1.0/src/rednote_cli/infra/platforms.py +4 -0
- rednote_cli-0.1.0/src/rednote_cli.egg-info/PKG-INFO +81 -0
- rednote_cli-0.1.0/src/rednote_cli.egg-info/SOURCES.txt +84 -0
- rednote_cli-0.1.0/src/rednote_cli.egg-info/dependency_links.txt +1 -0
- rednote_cli-0.1.0/src/rednote_cli.egg-info/entry_points.txt +2 -0
- rednote_cli-0.1.0/src/rednote_cli.egg-info/requires.txt +9 -0
- rednote_cli-0.1.0/src/rednote_cli.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rednote-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Rednote platform CLI
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: playwright==1.57.0
|
|
8
|
+
Requires-Dist: playwright-stealth==2.0.0
|
|
9
|
+
Requires-Dist: yt-dlp==2025.12.8
|
|
10
|
+
Requires-Dist: pydantic==2.12.2
|
|
11
|
+
Requires-Dist: typer>=0.12.0
|
|
12
|
+
Requires-Dist: rich>=13.7.0
|
|
13
|
+
Requires-Dist: loguru==0.7.3
|
|
14
|
+
Requires-Dist: croniter==6.0.0
|
|
15
|
+
Requires-Dist: fake_useragent==2.2.0
|
|
16
|
+
|
|
17
|
+
# rednote-cli
|
|
18
|
+
|
|
19
|
+
Rednote 平台专用 CLI 包(独立安装、独立升级)。
|
|
20
|
+
|
|
21
|
+
## 安装
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pipx install rednote-cli
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 快速开始
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
rednote-cli init runtime
|
|
31
|
+
rednote-cli doctor run --format json
|
|
32
|
+
rednote-cli account login
|
|
33
|
+
rednote-cli account status --format json
|
|
34
|
+
rednote-cli account list --only-active true
|
|
35
|
+
rednote-cli account list --only-active false
|
|
36
|
+
rednote-cli search note --keyword 旅行 --size 10 --sort-by latest --note-type image_text --format json
|
|
37
|
+
rednote-cli publish --target image --account user_001 --image-list /abs/a.jpg,/abs/b.jpg --title 标题 --content 正文 --format json
|
|
38
|
+
rednote-cli publish --target video --account user_001 --video /abs/demo.mp4 --title 标题 --content 正文 --format json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
命令结构(当前版本):
|
|
42
|
+
- `note` / `user` 作为详情查询入口,`search note|user` 作为统一搜索入口。
|
|
43
|
+
- 旧层级 `note get|search`、`user get|search` 已移除。
|
|
44
|
+
|
|
45
|
+
空参数行为(统一约定):
|
|
46
|
+
- `rednote-cli note`、`rednote-cli publish`、`rednote-cli account activate` 等缺少业务参数时,输出帮助文本并返回退出码 `0`。
|
|
47
|
+
- 非空调用但参数非法时,返回 JSON `INVALID_ARGS`(退出码 `2`)。
|
|
48
|
+
|
|
49
|
+
`account status` 会实时打开浏览器检查登录态并刷新账号存储(`is_logged_in/check_time/storage_state`),输出结构与 `account list --only-active false` 对齐。
|
|
50
|
+
|
|
51
|
+
`account list` 的 `--only-active` 支持显式 `true|false`(默认 `true`)。
|
|
52
|
+
|
|
53
|
+
发布能力说明:
|
|
54
|
+
- `publish --target image`:图文发布,支持本地/URL 图片,`--account` 必填。
|
|
55
|
+
- `publish --target video`:视频发布,支持本地/URL 视频(仅 `mp4/mov`),`--account` 必填。
|
|
56
|
+
|
|
57
|
+
## 自动化建议
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
rednote-cli --format json --trace-id req-001 <command> ...
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`--input`(面向 AI/Agent):
|
|
64
|
+
- 作用:从 JSON 文件或 stdin 读取参数,适合复杂命令的程序化调用。
|
|
65
|
+
- 优先级:显式 CLI 参数 > `--input` JSON > 默认值。
|
|
66
|
+
- 范围:`--input` 不是全局参数,仅 `search note|user`、`note`、`user`、`publish` 支持。
|
|
67
|
+
|
|
68
|
+
示例:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
rednote-cli --format json --trace-id req-001 search note --input payload.json
|
|
72
|
+
cat payload.json | rednote-cli --format json --trace-id req-002 publish --input - --account user_001
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 文档
|
|
76
|
+
|
|
77
|
+
完整文档请查看仓库根目录:
|
|
78
|
+
- `README.md`
|
|
79
|
+
- `DESIGN.md`
|
|
80
|
+
- `DEV.md`
|
|
81
|
+
- `USER_GUIDE.md`
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# rednote-cli
|
|
2
|
+
|
|
3
|
+
Rednote 平台专用 CLI 包(独立安装、独立升级)。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pipx install rednote-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 快速开始
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
rednote-cli init runtime
|
|
15
|
+
rednote-cli doctor run --format json
|
|
16
|
+
rednote-cli account login
|
|
17
|
+
rednote-cli account status --format json
|
|
18
|
+
rednote-cli account list --only-active true
|
|
19
|
+
rednote-cli account list --only-active false
|
|
20
|
+
rednote-cli search note --keyword 旅行 --size 10 --sort-by latest --note-type image_text --format json
|
|
21
|
+
rednote-cli publish --target image --account user_001 --image-list /abs/a.jpg,/abs/b.jpg --title 标题 --content 正文 --format json
|
|
22
|
+
rednote-cli publish --target video --account user_001 --video /abs/demo.mp4 --title 标题 --content 正文 --format json
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
命令结构(当前版本):
|
|
26
|
+
- `note` / `user` 作为详情查询入口,`search note|user` 作为统一搜索入口。
|
|
27
|
+
- 旧层级 `note get|search`、`user get|search` 已移除。
|
|
28
|
+
|
|
29
|
+
空参数行为(统一约定):
|
|
30
|
+
- `rednote-cli note`、`rednote-cli publish`、`rednote-cli account activate` 等缺少业务参数时,输出帮助文本并返回退出码 `0`。
|
|
31
|
+
- 非空调用但参数非法时,返回 JSON `INVALID_ARGS`(退出码 `2`)。
|
|
32
|
+
|
|
33
|
+
`account status` 会实时打开浏览器检查登录态并刷新账号存储(`is_logged_in/check_time/storage_state`),输出结构与 `account list --only-active false` 对齐。
|
|
34
|
+
|
|
35
|
+
`account list` 的 `--only-active` 支持显式 `true|false`(默认 `true`)。
|
|
36
|
+
|
|
37
|
+
发布能力说明:
|
|
38
|
+
- `publish --target image`:图文发布,支持本地/URL 图片,`--account` 必填。
|
|
39
|
+
- `publish --target video`:视频发布,支持本地/URL 视频(仅 `mp4/mov`),`--account` 必填。
|
|
40
|
+
|
|
41
|
+
## 自动化建议
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
rednote-cli --format json --trace-id req-001 <command> ...
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`--input`(面向 AI/Agent):
|
|
48
|
+
- 作用:从 JSON 文件或 stdin 读取参数,适合复杂命令的程序化调用。
|
|
49
|
+
- 优先级:显式 CLI 参数 > `--input` JSON > 默认值。
|
|
50
|
+
- 范围:`--input` 不是全局参数,仅 `search note|user`、`note`、`user`、`publish` 支持。
|
|
51
|
+
|
|
52
|
+
示例:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
rednote-cli --format json --trace-id req-001 search note --input payload.json
|
|
56
|
+
cat payload.json | rednote-cli --format json --trace-id req-002 publish --input - --account user_001
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 文档
|
|
60
|
+
|
|
61
|
+
完整文档请查看仓库根目录:
|
|
62
|
+
- `README.md`
|
|
63
|
+
- `DESIGN.md`
|
|
64
|
+
- `DEV.md`
|
|
65
|
+
- `USER_GUIDE.md`
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"setuptools>=68",
|
|
4
|
+
"wheel",
|
|
5
|
+
]
|
|
6
|
+
build-backend = "setuptools.build_meta"
|
|
7
|
+
|
|
8
|
+
[project]
|
|
9
|
+
name = "rednote-cli"
|
|
10
|
+
version = "0.1.0"
|
|
11
|
+
description = "Rednote platform CLI"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.11"
|
|
14
|
+
dependencies = [
|
|
15
|
+
"playwright==1.57.0",
|
|
16
|
+
"playwright-stealth==2.0.0",
|
|
17
|
+
"yt-dlp==2025.12.8",
|
|
18
|
+
"pydantic==2.12.2",
|
|
19
|
+
"typer>=0.12.0",
|
|
20
|
+
"rich>=13.7.0",
|
|
21
|
+
"loguru==0.7.3",
|
|
22
|
+
"croniter==6.0.0",
|
|
23
|
+
"fake_useragent==2.2.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
rednote-cli = "rednote_cli.cli.main:run"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools]
|
|
30
|
+
include-package-data = true
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
where = ["src"]
|
|
34
|
+
include = ["rednote_cli*"]
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
import sys
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_app_path(relative_path):
|
|
8
|
+
"""
|
|
9
|
+
获取资源绝对路径(兼容 Dev 和 Frozen 环境)
|
|
10
|
+
|
|
11
|
+
在 PyInstaller --onedir 模式下,macOS .app 的结构通常是:
|
|
12
|
+
OperatorRuntime.app/
|
|
13
|
+
Contents/
|
|
14
|
+
MacOS/
|
|
15
|
+
OperatorRuntime (可执行文件)
|
|
16
|
+
assets/ (如果 spec 中配置在同级)
|
|
17
|
+
Resources/ (标准 Mac 资源目录)
|
|
18
|
+
|
|
19
|
+
我们现在的 spec 配置是将 assets 放在可执行文件同级目录。
|
|
20
|
+
"""
|
|
21
|
+
if getattr(sys, 'frozen', False):
|
|
22
|
+
# 打包后的环境
|
|
23
|
+
bundle_dir = os.path.dirname(sys.executable)
|
|
24
|
+
return os.path.join(bundle_dir, relative_path)
|
|
25
|
+
else:
|
|
26
|
+
# 开发环境
|
|
27
|
+
return os.path.join(os.path.abspath("."), relative_path)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_system_chrome_path():
|
|
31
|
+
"""
|
|
32
|
+
获取系统已安装的 Chrome/Edge 路径,避免打包几百兆的浏览器。
|
|
33
|
+
优先查找 Chrome,其次 Edge。
|
|
34
|
+
"""
|
|
35
|
+
system = platform.system()
|
|
36
|
+
paths = []
|
|
37
|
+
|
|
38
|
+
if system == "Darwin": # macOS
|
|
39
|
+
paths = [
|
|
40
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
41
|
+
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
42
|
+
# 用户可能安装在用户目录下
|
|
43
|
+
os.path.expanduser("~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"),
|
|
44
|
+
]
|
|
45
|
+
elif system == "Windows":
|
|
46
|
+
paths = [
|
|
47
|
+
r"C:\Program Files\Google\Chrome\Application\chrome.exe",
|
|
48
|
+
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
|
|
49
|
+
r"C:\Program Files\Microsoft\Edge\Application\msedge.exe",
|
|
50
|
+
r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
for p in paths:
|
|
54
|
+
if os.path.exists(p):
|
|
55
|
+
return p
|
|
56
|
+
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def timestamp_to_str(ts, fmt='%Y-%m-%d %H:%M:%S', tz=None):
|
|
61
|
+
"""
|
|
62
|
+
自动识别10位或13位时间戳并转为日期字符串
|
|
63
|
+
"""
|
|
64
|
+
if not ts: return ts
|
|
65
|
+
|
|
66
|
+
# 如果是字符串类型的输入,先转为数字
|
|
67
|
+
try:
|
|
68
|
+
ts = int(ts)
|
|
69
|
+
except Exception:
|
|
70
|
+
return ts
|
|
71
|
+
|
|
72
|
+
# 核心逻辑:如果数值大于 1e11 (即 100,000,000,000),通常被认为是毫秒级
|
|
73
|
+
# 10位时间戳最大到 2286年 (9999999999)
|
|
74
|
+
if ts > 10000000000:
|
|
75
|
+
ts = ts / 1000
|
|
76
|
+
|
|
77
|
+
return datetime.fromtimestamp(ts, tz=tz).strftime(fmt)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _read_bool_env(name: str, default: bool) -> bool:
|
|
7
|
+
raw = os.getenv(name)
|
|
8
|
+
if raw is None:
|
|
9
|
+
return default
|
|
10
|
+
text = str(raw).strip().lower()
|
|
11
|
+
if text in {"1", "true", "yes", "on"}:
|
|
12
|
+
return True
|
|
13
|
+
if text in {"0", "false", "no", "off"}:
|
|
14
|
+
return False
|
|
15
|
+
return default
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_app_data_dir(app_name="OperatorRuntime"):
|
|
19
|
+
"""
|
|
20
|
+
Get the platform-specific user data directory.
|
|
21
|
+
"""
|
|
22
|
+
system = platform.system()
|
|
23
|
+
if system == "Windows":
|
|
24
|
+
return Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming")) / app_name
|
|
25
|
+
elif system == "Darwin":
|
|
26
|
+
return Path.home() / "Library" / "Application Support" / app_name
|
|
27
|
+
else:
|
|
28
|
+
# Linux / Unix
|
|
29
|
+
return Path.home() / ".local" / "share" / app_name
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Initialize paths
|
|
33
|
+
APP_DATA_DIR = get_app_data_dir()
|
|
34
|
+
try:
|
|
35
|
+
APP_DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
except PermissionError:
|
|
37
|
+
# Sandbox-friendly fallback for restricted environments (tests/CI).
|
|
38
|
+
APP_DATA_DIR = Path.cwd() / ".operator_runtime_data"
|
|
39
|
+
APP_DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
|
|
41
|
+
# Database path
|
|
42
|
+
DB_FILE = APP_DATA_DIR / "operator_runtime.db"
|
|
43
|
+
|
|
44
|
+
# Log path
|
|
45
|
+
LOG_DIR = APP_DATA_DIR / "logs"
|
|
46
|
+
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
LOG_FILE = LOG_DIR / "app.log"
|
|
48
|
+
|
|
49
|
+
# --- 调试配置 ---
|
|
50
|
+
# 默认行为:无头(生产)
|
|
51
|
+
# 开发调试可通过环境变量强制有头:
|
|
52
|
+
# - REDNOTE_CLI_HEADFUL=1
|
|
53
|
+
# - BROWSER_HEADFUL=1
|
|
54
|
+
BROWSER_HEADLESS = _read_bool_env("BROWSER_HEADLESS", True)
|
|
55
|
+
_BROWSER_HEADFUL_FORCED = _read_bool_env("REDNOTE_CLI_HEADFUL", False) or _read_bool_env("BROWSER_HEADFUL", False)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def is_browser_headful_forced() -> bool:
|
|
59
|
+
return _BROWSER_HEADFUL_FORCED
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def resolve_browser_headless(requested: bool | None = None) -> bool:
|
|
63
|
+
if _BROWSER_HEADFUL_FORCED:
|
|
64
|
+
return False
|
|
65
|
+
if requested is None:
|
|
66
|
+
return BROWSER_HEADLESS
|
|
67
|
+
return bool(requested)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def resolve_login_mode_headless(mode: str) -> bool:
|
|
71
|
+
normalized_mode = str(mode or "").strip().lower()
|
|
72
|
+
if normalized_mode == "local":
|
|
73
|
+
# Local login is interactive by design.
|
|
74
|
+
return resolve_browser_headless(False)
|
|
75
|
+
if normalized_mode == "remote":
|
|
76
|
+
# Remote login defaults to headless, but follows BROWSER_HEADLESS env.
|
|
77
|
+
return resolve_browser_headless(None)
|
|
78
|
+
return resolve_browser_headless()
|
|
79
|
+
|
|
80
|
+
# --- 浏览器环境配置 ---
|
|
81
|
+
# 建议与账号真实访问地区保持一致,减少环境指纹漂移。
|
|
82
|
+
BROWSER_LOCALE = os.getenv("BROWSER_LOCALE", "zh-CN")
|
|
83
|
+
BROWSER_TIMEZONE = os.getenv("BROWSER_TIMEZONE", "Asia/Shanghai")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Platform(Enum):
|
|
5
|
+
REDNOTE = "Rednote"
|
|
6
|
+
DOUYIN = "Douyin"
|
|
7
|
+
TIKTOK = "TikTok"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class XsecSource(Enum):
|
|
11
|
+
PC_FEED = "pc_feed" # 发现页 → 笔记;发现页 → 达人
|
|
12
|
+
PC_SEARCH = "pc_search" # 搜索页 → 笔记;搜索页 → 达人
|
|
13
|
+
PC_COLLECT = "pc_collect" # 达人-收藏页 → 笔记;达人-收藏页 → 达人
|
|
14
|
+
PC_LIKE = "pc_like" # 达人-点赞页 → 笔记;达人-点赞页 → 达人
|
|
15
|
+
PC_USER = "pc_user" # 达人-笔记页 → 笔记
|
|
16
|
+
PC_NOTE = "pc_note" # 笔记页 → 达人
|
|
17
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class PublishNoteException(Exception):
|
|
2
|
+
"""Base exception for publish-note workflow."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class InvalidPublishParameterError(PublishNoteException, ValueError):
|
|
6
|
+
"""Raised when publish-note inputs are invalid."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UnsupportedPublishTargetError(PublishNoteException):
|
|
10
|
+
"""Raised when publish target is unsupported."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PublishWorkflowNotReadyError(PublishNoteException):
|
|
14
|
+
"""Raised when target exists but workflow is not implemented yet."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PublishMediaPreparationError(PublishNoteException):
|
|
18
|
+
"""Raised when media validation/download fails."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PublishExecutionError(PublishNoteException):
|
|
22
|
+
"""Raised when publish execution fails unexpectedly."""
|
|
File without changes
|