beervid-app-cli 0.2.5 → 0.2.7
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.
- package/README.md +19 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.mjs +165 -73
- package/dist/index.d.ts +255 -0
- package/dist/index.mjs +1 -0
- package/package.json +15 -2
- package/skills/beervid-app-cli/FAQ.md +214 -0
- package/skills/beervid-app-cli/QUICKSTART.md +206 -0
- package/skills/beervid-app-cli/SKILL.md +52 -9
- package/skills/beervid-app-cli/docs/database-schema.md +12 -2
- package/skills/beervid-app-cli/docs/oauth-callback.md +92 -40
- package/skills/beervid-app-cli/docs/performance-and-limits.md +153 -0
- package/skills/beervid-app-cli/docs/security-best-practices.md +132 -0
- package/skills/beervid-app-cli/docs/testing-guide.md +689 -0
- package/skills/beervid-app-cli/docs/troubleshooting.md +468 -0
- package/skills/beervid-app-cli/example/express/README.md +6 -0
- package/skills/beervid-app-cli/example/express/server.ts +31 -9
- package/skills/beervid-app-cli/example/nextjs/README.md +6 -0
- package/skills/beervid-app-cli/example/nextjs/app/api/oauth/callback/route.ts +22 -6
- package/skills/beervid-app-cli/example/nextjs/app/api/oauth/url/route.ts +3 -2
- package/skills/beervid-app-cli/example/nextjs/app/api/publish/tts/route.ts +2 -2
- package/skills/beervid-app-cli/example/standard/README.md +6 -0
- package/skills/beervid-app-cli/example/standard/get-oauth-url.ts +3 -2
- package/skills/beervid-app-cli/example/standard/tts-publish-flow.ts +1 -1
- package/skills/beervid-app-cli/references/api-reference.md +37 -11
- package/skills/beervid-app-cli/skill.json +36 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
interface OpenApiResponse<T> {
|
|
2
|
+
code: number;
|
|
3
|
+
message: string;
|
|
4
|
+
data: T;
|
|
5
|
+
error: boolean;
|
|
6
|
+
success: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** API-level account type (uppercase) */
|
|
9
|
+
type AccountType = 'TT' | 'TTS';
|
|
10
|
+
/** CLI --type param for OAuth (lowercase) */
|
|
11
|
+
type OAuthAccountType = 'tt' | 'tts';
|
|
12
|
+
type PublishType = 'normal' | 'shoppable';
|
|
13
|
+
type UploadType = 'normal' | 'tts';
|
|
14
|
+
type ProductType = 'shop' | 'showcase' | 'all';
|
|
15
|
+
type VideoStatus = 'PROCESSING_DOWNLOAD' | 'PUBLISH_COMPLETE' | 'FAILED' | string;
|
|
16
|
+
type TtOAuthUrlData = string;
|
|
17
|
+
interface TtsOAuthUrlData {
|
|
18
|
+
crossBorderUrl: string;
|
|
19
|
+
}
|
|
20
|
+
interface UploadTokenData {
|
|
21
|
+
uploadToken: string;
|
|
22
|
+
expiresIn: number;
|
|
23
|
+
}
|
|
24
|
+
interface NormalUploadResult {
|
|
25
|
+
fileUrl: string;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
interface TtsUploadResult {
|
|
29
|
+
videoFileId: string;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
interface NormalPublishResult {
|
|
33
|
+
shareId: string;
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
interface NormalPublishOptions {
|
|
37
|
+
caption?: string;
|
|
38
|
+
isBrandOrganic?: boolean;
|
|
39
|
+
isBrandedContent?: boolean;
|
|
40
|
+
disableComment?: boolean;
|
|
41
|
+
disableDuet?: boolean;
|
|
42
|
+
disableStitch?: boolean;
|
|
43
|
+
thumbnailOffset?: number;
|
|
44
|
+
}
|
|
45
|
+
interface ShoppablePublishResult {
|
|
46
|
+
videoId: string;
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
interface VideoStatusData {
|
|
50
|
+
status?: VideoStatus;
|
|
51
|
+
Status?: VideoStatus;
|
|
52
|
+
reason?: string;
|
|
53
|
+
post_ids?: string[];
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
}
|
|
56
|
+
interface RawVideoItem {
|
|
57
|
+
itemId?: string;
|
|
58
|
+
item_id?: string;
|
|
59
|
+
videoViews?: number;
|
|
60
|
+
video_views?: number;
|
|
61
|
+
likes?: number;
|
|
62
|
+
comments?: number;
|
|
63
|
+
shares?: number;
|
|
64
|
+
thumbnailUrl?: string;
|
|
65
|
+
thumbnail_url?: string;
|
|
66
|
+
shareUrl?: string;
|
|
67
|
+
share_url?: string;
|
|
68
|
+
[key: string]: unknown;
|
|
69
|
+
}
|
|
70
|
+
interface NormalizedVideoItem {
|
|
71
|
+
itemId: string | undefined;
|
|
72
|
+
videoViews: number;
|
|
73
|
+
likes: number;
|
|
74
|
+
comments: number;
|
|
75
|
+
shares: number;
|
|
76
|
+
thumbnailUrl: string;
|
|
77
|
+
shareUrl: string;
|
|
78
|
+
}
|
|
79
|
+
interface QueryVideoData {
|
|
80
|
+
videoList?: RawVideoItem[];
|
|
81
|
+
videos?: RawVideoItem[];
|
|
82
|
+
[key: string]: unknown;
|
|
83
|
+
}
|
|
84
|
+
interface RawProductItem {
|
|
85
|
+
id: string;
|
|
86
|
+
title: string;
|
|
87
|
+
price?: unknown;
|
|
88
|
+
images?: string[];
|
|
89
|
+
salesCount?: number;
|
|
90
|
+
brandName?: string;
|
|
91
|
+
shopName?: string;
|
|
92
|
+
source?: string;
|
|
93
|
+
reviewStatus?: string;
|
|
94
|
+
inventoryStatus?: string;
|
|
95
|
+
[key: string]: unknown;
|
|
96
|
+
}
|
|
97
|
+
interface NormalizedProductItem {
|
|
98
|
+
id: string;
|
|
99
|
+
title: string;
|
|
100
|
+
price: unknown;
|
|
101
|
+
images: string[];
|
|
102
|
+
salesCount: number;
|
|
103
|
+
brandName: string;
|
|
104
|
+
shopName: string;
|
|
105
|
+
source: string;
|
|
106
|
+
reviewStatus: string | undefined;
|
|
107
|
+
inventoryStatus: string | undefined;
|
|
108
|
+
}
|
|
109
|
+
interface ProductPageData {
|
|
110
|
+
nextPageToken?: string | null;
|
|
111
|
+
products?: RawProductItem[];
|
|
112
|
+
[key: string]: unknown;
|
|
113
|
+
}
|
|
114
|
+
interface ProductCursor {
|
|
115
|
+
shopToken: string | null;
|
|
116
|
+
showcaseToken: string | null;
|
|
117
|
+
}
|
|
118
|
+
interface WorkflowWarning {
|
|
119
|
+
code: string;
|
|
120
|
+
message: string;
|
|
121
|
+
}
|
|
122
|
+
interface NormalizedVideoQueryResult {
|
|
123
|
+
videos: NormalizedVideoItem[];
|
|
124
|
+
raw: QueryVideoData;
|
|
125
|
+
}
|
|
126
|
+
interface TTFlowStatusResult {
|
|
127
|
+
pollCount: number;
|
|
128
|
+
finalStatus: string;
|
|
129
|
+
reason: string | null;
|
|
130
|
+
postIds: string[];
|
|
131
|
+
raw: VideoStatusData | null;
|
|
132
|
+
}
|
|
133
|
+
interface TTFlowQueryResult extends NormalizedVideoQueryResult {
|
|
134
|
+
attempts: number;
|
|
135
|
+
}
|
|
136
|
+
interface TTWorkflowResult {
|
|
137
|
+
flowType: 'tt';
|
|
138
|
+
upload: NormalUploadResult;
|
|
139
|
+
publish: NormalPublishResult;
|
|
140
|
+
status: TTFlowStatusResult;
|
|
141
|
+
videoId: string | null;
|
|
142
|
+
query: TTFlowQueryResult | null;
|
|
143
|
+
warnings: WorkflowWarning[];
|
|
144
|
+
}
|
|
145
|
+
interface ProductQuerySummary {
|
|
146
|
+
productType: ProductType;
|
|
147
|
+
pageSize: number;
|
|
148
|
+
pagesScanned: number;
|
|
149
|
+
productCount: number;
|
|
150
|
+
nextCursor: string | null;
|
|
151
|
+
reachedPageLimit: boolean;
|
|
152
|
+
failedSources: string[];
|
|
153
|
+
}
|
|
154
|
+
interface SelectedProductSummary {
|
|
155
|
+
selectionMode: 'manual' | 'interactive' | 'automatic';
|
|
156
|
+
id: string;
|
|
157
|
+
title: string;
|
|
158
|
+
salesCount: number;
|
|
159
|
+
source: string;
|
|
160
|
+
price: unknown;
|
|
161
|
+
brandName: string;
|
|
162
|
+
shopName: string;
|
|
163
|
+
}
|
|
164
|
+
interface TTSWorkflowResult {
|
|
165
|
+
flowType: 'tts';
|
|
166
|
+
productQuery: ProductQuerySummary;
|
|
167
|
+
selectedProduct: SelectedProductSummary;
|
|
168
|
+
upload: TtsUploadResult;
|
|
169
|
+
publish: ShoppablePublishResult;
|
|
170
|
+
warnings: WorkflowWarning[];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
type CliCommandName = 'config' | 'get-oauth-url' | 'get-account-info' | 'upload' | 'publish' | 'poll-status' | 'query-video' | 'query-products' | 'publish-tt-flow' | 'publish-tts-flow';
|
|
174
|
+
interface ConfigCommandOptions {
|
|
175
|
+
appKey?: string;
|
|
176
|
+
baseUrl?: string;
|
|
177
|
+
show?: boolean;
|
|
178
|
+
}
|
|
179
|
+
interface GetOAuthUrlCommandOptions {
|
|
180
|
+
type?: OAuthAccountType | string;
|
|
181
|
+
}
|
|
182
|
+
interface GetAccountInfoCommandOptions {
|
|
183
|
+
type?: AccountType | string;
|
|
184
|
+
accountId?: string;
|
|
185
|
+
}
|
|
186
|
+
interface UploadCommandOptions {
|
|
187
|
+
file?: string;
|
|
188
|
+
type?: UploadType | string;
|
|
189
|
+
creatorId?: string;
|
|
190
|
+
token?: string;
|
|
191
|
+
}
|
|
192
|
+
interface PublishCommandOptions {
|
|
193
|
+
type?: PublishType | string;
|
|
194
|
+
businessId?: string;
|
|
195
|
+
videoUrl?: string;
|
|
196
|
+
creatorId?: string;
|
|
197
|
+
fileId?: string;
|
|
198
|
+
productId?: string;
|
|
199
|
+
productTitle?: string;
|
|
200
|
+
caption?: string;
|
|
201
|
+
brandOrganic?: boolean;
|
|
202
|
+
brandedContent?: boolean;
|
|
203
|
+
disableComment?: boolean;
|
|
204
|
+
disableDuet?: boolean;
|
|
205
|
+
disableStitch?: boolean;
|
|
206
|
+
thumbnailOffset?: string;
|
|
207
|
+
}
|
|
208
|
+
interface PollStatusCommandOptions {
|
|
209
|
+
businessId?: string;
|
|
210
|
+
shareId?: string;
|
|
211
|
+
interval?: string;
|
|
212
|
+
maxPolls?: string;
|
|
213
|
+
}
|
|
214
|
+
interface QueryVideoCommandOptions {
|
|
215
|
+
businessId?: string;
|
|
216
|
+
itemIds?: string;
|
|
217
|
+
cursor?: string;
|
|
218
|
+
maxCount?: string;
|
|
219
|
+
}
|
|
220
|
+
interface QueryProductsCommandOptions {
|
|
221
|
+
creatorId?: string;
|
|
222
|
+
productType?: ProductType | string;
|
|
223
|
+
pageSize?: string;
|
|
224
|
+
cursor?: string;
|
|
225
|
+
}
|
|
226
|
+
interface PublishTTFlowCommandOptions {
|
|
227
|
+
businessId?: string;
|
|
228
|
+
file?: string;
|
|
229
|
+
caption?: string;
|
|
230
|
+
token?: string;
|
|
231
|
+
brandOrganic?: boolean;
|
|
232
|
+
brandedContent?: boolean;
|
|
233
|
+
disableComment?: boolean;
|
|
234
|
+
disableDuet?: boolean;
|
|
235
|
+
disableStitch?: boolean;
|
|
236
|
+
thumbnailOffset?: string;
|
|
237
|
+
interval?: string;
|
|
238
|
+
maxPolls?: string;
|
|
239
|
+
queryInterval?: string;
|
|
240
|
+
queryMaxAttempts?: string;
|
|
241
|
+
}
|
|
242
|
+
interface PublishTTSFlowCommandOptions {
|
|
243
|
+
creatorId?: string;
|
|
244
|
+
file?: string;
|
|
245
|
+
caption?: string;
|
|
246
|
+
token?: string;
|
|
247
|
+
productType?: ProductType | string;
|
|
248
|
+
pageSize?: string;
|
|
249
|
+
maxProductPages?: string;
|
|
250
|
+
productId?: string;
|
|
251
|
+
productTitle?: string;
|
|
252
|
+
interactive?: boolean;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export type { AccountType, CliCommandName, ConfigCommandOptions, GetAccountInfoCommandOptions, GetOAuthUrlCommandOptions, NormalPublishOptions, NormalPublishResult, NormalUploadResult, NormalizedProductItem, NormalizedVideoItem, NormalizedVideoQueryResult, OAuthAccountType, OpenApiResponse, PollStatusCommandOptions, ProductCursor, ProductPageData, ProductQuerySummary, ProductType, PublishCommandOptions, PublishTTFlowCommandOptions, PublishTTSFlowCommandOptions, PublishType, QueryProductsCommandOptions, QueryVideoCommandOptions, QueryVideoData, RawProductItem, RawVideoItem, SelectedProductSummary, ShoppablePublishResult, TTFlowQueryResult, TTFlowStatusResult, TTSWorkflowResult, TTWorkflowResult, TtOAuthUrlData, TtsOAuthUrlData, TtsUploadResult, UploadCommandOptions, UploadTokenData, UploadType, VideoStatus, VideoStatusData, WorkflowWarning };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/package.json
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beervid-app-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "BEERVID App CLI — TikTok video publish, account auth, and data query",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/Lupeiwen0/beervid-app-cli"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
6
11
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
12
|
+
"node": ">=22.0.0"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.mjs"
|
|
18
|
+
}
|
|
8
19
|
},
|
|
9
20
|
"bin": {
|
|
10
21
|
"beervid": "dist/cli.mjs"
|
|
@@ -19,6 +30,8 @@
|
|
|
19
30
|
"dev": "tsx src/cli.ts",
|
|
20
31
|
"test": "vitest run",
|
|
21
32
|
"typecheck": "tsc --noEmit",
|
|
33
|
+
"sync:skill-version": "node scripts/sync-skill-version.mjs",
|
|
34
|
+
"version": "npm run sync:skill-version",
|
|
22
35
|
"prepublishOnly": "npm run build"
|
|
23
36
|
},
|
|
24
37
|
"dependencies": {
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# 常见问题(FAQ)
|
|
2
|
+
|
|
3
|
+
本文档只保留 BEERVID Open API 接入中的高频误区和补充说明;首次接入请先看 [QUICKSTART.md](./QUICKSTART.md)。
|
|
4
|
+
|
|
5
|
+
## 目录
|
|
6
|
+
|
|
7
|
+
1. [基础概念](#基础概念)
|
|
8
|
+
2. [账号授权](#账号授权)
|
|
9
|
+
3. [视频上传](#视频上传)
|
|
10
|
+
4. [视频发布](#视频发布)
|
|
11
|
+
5. [数据查询](#数据查询)
|
|
12
|
+
6. [CLI 使用](#cli-使用)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 基础概念
|
|
17
|
+
|
|
18
|
+
### Q: TT 和 TTS 有什么区别?
|
|
19
|
+
|
|
20
|
+
**A:**
|
|
21
|
+
- **TT**:普通 TikTok 账号,用于普通视频发布,也支持视频数据查询
|
|
22
|
+
- **TTS**:TikTok Shop 跨境账号,用于挂车视频发布,不支持视频数据查询
|
|
23
|
+
|
|
24
|
+
### Q: businessId 和 creatorUserOpenId 有什么区别?
|
|
25
|
+
|
|
26
|
+
**A:**
|
|
27
|
+
- `businessId`:TT 账号的业务 ID,来自 OAuth 回调里的 `ttAbId`
|
|
28
|
+
- `creatorUserOpenId`:TTS 账号的 OpenId,来自 OAuth 回调里的 `ttsAbId`
|
|
29
|
+
|
|
30
|
+
它们都是后续 API 的核心入参,但分别服务于不同账号体系。
|
|
31
|
+
|
|
32
|
+
### Q: 同一个达人既要做 TTS 挂车发布,又要查视频数据,应该怎么授权?
|
|
33
|
+
|
|
34
|
+
**A:**
|
|
35
|
+
- 需要分别完成两次授权:一次 TTS,一次 TT
|
|
36
|
+
- TTS 授权用于挂车发布、商品查询
|
|
37
|
+
- TT 授权用于视频数据查询
|
|
38
|
+
- 不能只授权 TTS 就去查视频数据
|
|
39
|
+
|
|
40
|
+
### Q: TT 和 TTS 账号怎么建立关联?
|
|
41
|
+
|
|
42
|
+
**A:**
|
|
43
|
+
- 官方当前没有提供 `uno_id` 这类可直接关联 TT/TTS 的稳定字段
|
|
44
|
+
- 当前推荐在授权完成后分别调用 `account/info`
|
|
45
|
+
- 用两边返回的 `username` 作为你方系统内的关联键,建立 TT 和 TTS 的软关联
|
|
46
|
+
- 真正调用 API 时仍然分别使用 `businessId` 和 `creatorUserOpenId`
|
|
47
|
+
|
|
48
|
+
### Q: API 是否支持国内 TikTok(抖音)?
|
|
49
|
+
|
|
50
|
+
**A:**
|
|
51
|
+
当前这套 Open API 面向国际版 TikTok 和 TikTok Shop 跨境账号;如果是抖音场景,不要默认这套接口可直接复用。
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 账号授权
|
|
56
|
+
|
|
57
|
+
### Q: 用户授权后,如何拿到 ttAbId 或 ttsAbId?
|
|
58
|
+
|
|
59
|
+
**A:**
|
|
60
|
+
从回调 URL 的 `state` 参数中解析 JSON:
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
const urlParams = new URLSearchParams(window.location.search)
|
|
64
|
+
const stateJson = decodeURIComponent(urlParams.get('state'))
|
|
65
|
+
const state = JSON.parse(stateJson)
|
|
66
|
+
|
|
67
|
+
const ttAbId = state.ttAbId
|
|
68
|
+
const ttsAbId = state.ttsAbId
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
更完整的处理方式见 [oauth-callback.md](./docs/oauth-callback.md)。
|
|
72
|
+
|
|
73
|
+
### Q: 一个用户可以绑定多个 TikTok 账号吗?
|
|
74
|
+
|
|
75
|
+
**A:**
|
|
76
|
+
可以。每次授权返回的 `ttAbId` / `ttsAbId` 都是独立账号标识,你可以在自己系统里做一对多关联。
|
|
77
|
+
|
|
78
|
+
如果同一达人既授权了 TT 又授权了 TTS,也建议保存成两条独立记录,再通过 `username` 建立关联关系。
|
|
79
|
+
|
|
80
|
+
### Q: 如何解绑账号?
|
|
81
|
+
|
|
82
|
+
**A:**
|
|
83
|
+
当前 API 不提供单独的解绑接口。通常做法是在你自己的系统中删除或停用对应账号记录。
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 视频上传
|
|
88
|
+
|
|
89
|
+
### Q: `--file` 支持本地路径还是 URL?
|
|
90
|
+
|
|
91
|
+
**A:**
|
|
92
|
+
两者都支持:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
beervid upload --file ./video.mp4
|
|
96
|
+
beervid upload --file https://example.com/video.mp4
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
如果传 URL,当前 CLI 会先下载再上传。
|
|
100
|
+
|
|
101
|
+
### Q: 上传凭证的有效期是多久?
|
|
102
|
+
|
|
103
|
+
**A:**
|
|
104
|
+
当前项目参考里使用的是 `expiresIn: 1800`,也就是 30 分钟。稳妥做法是每次上传前重新获取,不要长期缓存旧 token。
|
|
105
|
+
|
|
106
|
+
### Q: 如何实现上传进度显示?
|
|
107
|
+
|
|
108
|
+
**A:**
|
|
109
|
+
如果你不是直接用 CLI,而是自己封装上传,当前更适合用 XHR 做进度监听,而不是裸 `fetch`。
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 视频发布
|
|
114
|
+
|
|
115
|
+
### Q: 为什么 TT 发布需要轮询,TTS 通常不需要?
|
|
116
|
+
|
|
117
|
+
**A:**
|
|
118
|
+
- **TT**:发布接口先返回 `shareId`,后续还要轮询状态,直到 `PUBLISH_COMPLETE` 且 `post_ids` 非空
|
|
119
|
+
- **TTS**:挂车发布接口通常直接返回 `videoId`
|
|
120
|
+
|
|
121
|
+
### Q: TT 发布成功的判定条件是什么?
|
|
122
|
+
|
|
123
|
+
**A:**
|
|
124
|
+
不是只看 `status === PUBLISH_COMPLETE`,还要同时满足 `post_ids` 非空。只有这两个条件同时成立,才能把 `post_ids[0]` 当成真正的 `videoId` 使用。
|
|
125
|
+
|
|
126
|
+
### Q: TTS 发布时 productTitle 为什么要截断?
|
|
127
|
+
|
|
128
|
+
**A:**
|
|
129
|
+
当前 OpenAPI 约束是 `productTitle` 最多 30 字符,超出要提前截断:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const productTitle = originalTitle.length > 30
|
|
133
|
+
? originalTitle.slice(0, 30)
|
|
134
|
+
: originalTitle
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Q: 发布失败后可以直接重试吗?
|
|
138
|
+
|
|
139
|
+
**A:**
|
|
140
|
+
读接口和写接口要分开看:
|
|
141
|
+
- `query-products`、`query-video`、`poll-status` 这类读操作适合退避重试
|
|
142
|
+
- `publish` 这类写操作先考虑幂等,再决定是否补偿重试
|
|
143
|
+
|
|
144
|
+
详见 [retry-and-idempotency.md](./docs/retry-and-idempotency.md)。
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 数据查询
|
|
149
|
+
|
|
150
|
+
### Q: TTS 账号可以查询视频数据吗?
|
|
151
|
+
|
|
152
|
+
**A:**
|
|
153
|
+
不可以。视频数据查询只适用于 TT 账号。
|
|
154
|
+
|
|
155
|
+
如果你要查询某个 TTS 达人的视频数据,需要额外为同一达人完成 TT 授权,再使用 TT 的 `businessId` 调用 `query-video`。
|
|
156
|
+
|
|
157
|
+
### Q: `query-video` 一定要传 itemIds 吗?
|
|
158
|
+
|
|
159
|
+
**A:**
|
|
160
|
+
不一定。当前 CLI 既支持指定 `itemIds`:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
beervid query-video --business-id biz_123 --item-ids 7123,7124
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
也支持按分页方式查列表:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
beervid query-video --business-id biz_123 --cursor 0 --max-count 20
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Q: 商品分页里的 cursor 是怎么来的?
|
|
173
|
+
|
|
174
|
+
**A:**
|
|
175
|
+
当前实现不是直接透传后端的单个 token,而是把:
|
|
176
|
+
|
|
177
|
+
- `shopToken`
|
|
178
|
+
- `showcaseToken`
|
|
179
|
+
|
|
180
|
+
组成一个 JSON 对象,再做 base64 编码。这样可以同时追踪 `shop` 和 `showcase` 两个来源的分页进度。
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## CLI 使用
|
|
185
|
+
|
|
186
|
+
### Q: 配置保存在哪里?
|
|
187
|
+
|
|
188
|
+
**A:**
|
|
189
|
+
默认保存在 `~/.beervid/config.json`。
|
|
190
|
+
|
|
191
|
+
### Q: 环境变量和配置文件哪个优先级高?
|
|
192
|
+
|
|
193
|
+
**A:**
|
|
194
|
+
优先级是:
|
|
195
|
+
|
|
196
|
+
`环境变量 > 配置文件 > 默认值`
|
|
197
|
+
|
|
198
|
+
### Q: CLI 命令执行失败时先看什么?
|
|
199
|
+
|
|
200
|
+
**A:**
|
|
201
|
+
优先看这三步:
|
|
202
|
+
|
|
203
|
+
1. `beervid config --show`
|
|
204
|
+
2. `beervid get-oauth-url --type tt`
|
|
205
|
+
3. 查看 [troubleshooting.md](./docs/troubleshooting.md)
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 相关文档
|
|
210
|
+
|
|
211
|
+
- [快速开始指南](./QUICKSTART.md)
|
|
212
|
+
- [完整 API 参考](./references/api-reference.md)
|
|
213
|
+
- [OAuth 回调](./docs/oauth-callback.md)
|
|
214
|
+
- [故障排查指南](./docs/troubleshooting.md)
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# 快速开始指南
|
|
2
|
+
|
|
3
|
+
5 分钟从零到发布第一个 TikTok 视频。
|
|
4
|
+
|
|
5
|
+
## 前置准备
|
|
6
|
+
|
|
7
|
+
1. **获取 API Key**
|
|
8
|
+
- 联系 BEERVID 平台获取 `BEERVID_APP_KEY`
|
|
9
|
+
- 确保你的应用已在 BEERVID 平台注册
|
|
10
|
+
|
|
11
|
+
2. **安装 CLI**
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g beervid-app-cli
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
3. **配置 API Key**
|
|
17
|
+
```bash
|
|
18
|
+
beervid config --app-key "your-api-key-here"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 场景一:发布普通视频(TT 账号)
|
|
22
|
+
|
|
23
|
+
### 步骤 1:获取授权链接
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
beervid get-oauth-url --type tt
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
输出示例:
|
|
30
|
+
```json
|
|
31
|
+
"https://www.tiktok.com/v2/auth/authorize?client_key=..."
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 步骤 2:用户授权
|
|
35
|
+
|
|
36
|
+
1. 将上面的 URL 发送给用户
|
|
37
|
+
2. 用户点击授权后,会跳转到你配置的回调地址
|
|
38
|
+
3. 从回调 URL 的 `state` 参数中解析 JSON,获取 `ttAbId`
|
|
39
|
+
|
|
40
|
+
回调示例:
|
|
41
|
+
```
|
|
42
|
+
https://your-domain.com/callback?state=%7B%22ttAbId%22%3A%227281234567890%22%7D
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
解析后得到:
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"ttAbId": "7281234567890"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 步骤 3:一键发布视频
|
|
53
|
+
|
|
54
|
+
使用 `ttAbId` 作为 `businessId` 发布视频:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
beervid publish-tt-flow \
|
|
58
|
+
--business-id "7281234567890" \
|
|
59
|
+
--file ./my-video.mp4 \
|
|
60
|
+
--caption "我的第一个视频"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
这个命令会自动完成:
|
|
64
|
+
- 获取上传凭证
|
|
65
|
+
- 上传视频文件
|
|
66
|
+
- 发布视频
|
|
67
|
+
- 轮询发布状态
|
|
68
|
+
- 查询视频数据
|
|
69
|
+
|
|
70
|
+
输出示例:
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"upload": {
|
|
74
|
+
"fileUrl": "https://cdn.beervid.ai/uploads/xxx.mp4"
|
|
75
|
+
},
|
|
76
|
+
"publish": {
|
|
77
|
+
"shareId": "share_abc123"
|
|
78
|
+
},
|
|
79
|
+
"status": {
|
|
80
|
+
"status": "PUBLISH_COMPLETE",
|
|
81
|
+
"post_ids": ["7123456789012345678"]
|
|
82
|
+
},
|
|
83
|
+
"query": {
|
|
84
|
+
"videoList": [
|
|
85
|
+
{
|
|
86
|
+
"itemId": "7123456789012345678",
|
|
87
|
+
"shareUrl": "https://www.tiktok.com/@username/video/7123456789012345678"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
完成!你的第一个视频已成功发布到 TikTok。
|
|
95
|
+
|
|
96
|
+
## 场景二:发布挂车视频(TTS 账号)
|
|
97
|
+
|
|
98
|
+
### 步骤 1:获取授权链接
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
beervid get-oauth-url --type tts
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 步骤 2:用户授权
|
|
105
|
+
|
|
106
|
+
从回调 URL 的 `state` 参数中解析 `ttsAbId`:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"ttsAbId": "open_user_abc123"
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 步骤 3:一键发布挂车视频
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
beervid publish-tts-flow \
|
|
118
|
+
--creator-id "open_user_abc123" \
|
|
119
|
+
--file ./product-video.mp4
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
CLI 会自动:
|
|
123
|
+
1. 查询可用商品列表
|
|
124
|
+
2. 自动选择一个可发布商品;如果你想手动挑选,请加 `--interactive`
|
|
125
|
+
3. 上传视频
|
|
126
|
+
4. 发布挂车视频
|
|
127
|
+
|
|
128
|
+
输出示例:
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"products": [
|
|
132
|
+
{
|
|
133
|
+
"products": [
|
|
134
|
+
{
|
|
135
|
+
"id": "prod_789",
|
|
136
|
+
"title": "Premium Widget"
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
"selectedProduct": {
|
|
142
|
+
"id": "prod_789",
|
|
143
|
+
"title": "Premium Widget"
|
|
144
|
+
},
|
|
145
|
+
"upload": {
|
|
146
|
+
"videoFileId": "vf_abc123def456"
|
|
147
|
+
},
|
|
148
|
+
"publish": {
|
|
149
|
+
"videoId": "7234567890123456789"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 场景三:同一达人既要挂车发布,又要查询视频数据
|
|
155
|
+
|
|
156
|
+
如果这个达人是 TTS 账号,并且你还想查询该账号的视频播放、点赞、评论等数据,需要额外再完成一次 TT 授权。
|
|
157
|
+
|
|
158
|
+
### 步骤 1:先授权 TTS,用于挂车发布
|
|
159
|
+
|
|
160
|
+
拿到 `ttsAbId`,作为 `creatorUserOpenId` 使用:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"ttsAbId": "open_user_abc123"
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 步骤 2:再授权 TT,用于视频数据查询
|
|
169
|
+
|
|
170
|
+
拿到 `ttAbId`,作为 `businessId` 使用:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"ttAbId": "7281234567890"
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 步骤 3:分别调用账号详情接口,建立本地关联
|
|
179
|
+
|
|
180
|
+
官方当前没有提供 `uno_id` 这种可直接关联 TT/TTS 的字段,推荐调用 `account/info` 后,用 `username` 作为当前的关联键:
|
|
181
|
+
|
|
182
|
+
```text
|
|
183
|
+
TTS account/info -> username = creator_name
|
|
184
|
+
TT account/info -> username = creator_name
|
|
185
|
+
|
|
186
|
+
=> 在你方系统里建立一条 TT <-> TTS 关联
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### 步骤 4:按场景使用不同 ID
|
|
190
|
+
|
|
191
|
+
- 挂车发布:使用 `creatorUserOpenId`
|
|
192
|
+
- 商品查询:使用 `creatorUserOpenId`
|
|
193
|
+
- 视频数据查询:使用 `businessId`
|
|
194
|
+
|
|
195
|
+
## 下一步
|
|
196
|
+
|
|
197
|
+
- 查看 [SKILL.md](./SKILL.md) 了解完整功能
|
|
198
|
+
- 查看 [references/api-reference.md](./references/api-reference.md) 了解 API 详情
|
|
199
|
+
- 查看 [docs/](./docs/) 了解生产环境部署建议
|
|
200
|
+
- 查看 [example/](./example/) 了解代码集成示例
|
|
201
|
+
|
|
202
|
+
## 需要帮助?
|
|
203
|
+
|
|
204
|
+
- 查看 [FAQ.md](./FAQ.md) 常见问题
|
|
205
|
+
- 查看 [TROUBLESHOOTING.md](./docs/troubleshooting.md) 故障排查
|
|
206
|
+
- 提交 Issue: https://github.com/Lupeiwen0/beervid-app-cli/issues
|