upload-post 2.9.0 → 2.10.0
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 +91 -0
- package/index.d.ts +205 -4
- package/index.js +177 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -295,8 +295,80 @@ const liPages = await client.getLinkedinPages('my-profile');
|
|
|
295
295
|
|
|
296
296
|
// Get Pinterest boards for a profile
|
|
297
297
|
const boards = await client.getPinterestBoards('my-profile');
|
|
298
|
+
|
|
299
|
+
// TikTok: trending Commercial Music Library tracks
|
|
300
|
+
const music = await client.getTiktokTrendingMusic('my-profile', {
|
|
301
|
+
genre: 'POP',
|
|
302
|
+
countryCode: 'ES',
|
|
303
|
+
dateRange: '7DAY', // 1DAY, 7DAY, 30DAY, 90DAY
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// TikTok: find a track by song or artist
|
|
307
|
+
const found = await client.searchTiktokMusic('my-profile', {
|
|
308
|
+
q: 'bad bunny',
|
|
309
|
+
countryCode: 'ES',
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// TikTok: search locations to tag
|
|
313
|
+
const locations = await client.getTiktokLocations('my-profile', 'Madrid');
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## TikTok music, location, cover and drafts
|
|
317
|
+
|
|
318
|
+
> **Capabilities.** These options are available on connections that declare the
|
|
319
|
+
> matching capability — `music`, `location`, `cover_image`, `draft` — in the
|
|
320
|
+
> `capabilities` array of the TikTok account returned by `listUsers()`
|
|
321
|
+
> (other values you may see there: `cover_timestamp`, `photo_privacy`,
|
|
322
|
+
> `video_privacy`, `inbox_fallback`, `profile_analytics`)
|
|
323
|
+
> (`GET /api/uploadposts/users`). If your connection does not have it, the field
|
|
324
|
+
> is ignored, the post still publishes, and the response includes a per-field
|
|
325
|
+
> `warnings` entry — reconnect the TikTok account to enable it.
|
|
326
|
+
|
|
327
|
+
```javascript
|
|
328
|
+
// 1. Pick a track and a place
|
|
329
|
+
const { tracks } = await client.getTiktokTrendingMusic('my-profile', { countryCode: 'ES' });
|
|
330
|
+
// ...or find one by name. TikTok has no music search endpoint, so this searches
|
|
331
|
+
// the trending charts Upload-Post caches, not TikTok's whole catalogue.
|
|
332
|
+
// const { tracks } = await client.searchTiktokMusic('my-profile', { q: 'bossa', countryCode: 'ES' });
|
|
333
|
+
const { locations } = await client.getTiktokLocations('my-profile', 'Madrid');
|
|
334
|
+
|
|
335
|
+
// 2. Publish with them
|
|
336
|
+
await client.upload('./video.mp4', {
|
|
337
|
+
title: 'Shot in Madrid',
|
|
338
|
+
user: 'my-profile',
|
|
339
|
+
platforms: ['tiktok'],
|
|
340
|
+
|
|
341
|
+
tiktokMusicId: tracks[0].id,
|
|
342
|
+
tiktokMusicVolume: 70, // 0-100, defaults to 50 when music is set
|
|
343
|
+
tiktokMusicStart: 0, // ms
|
|
344
|
+
tiktokMusicEnd: 15000, // ms
|
|
345
|
+
tiktokOriginalSoundVolume: 30, // 0-100, defaults to 50 so the original audio is not muted
|
|
346
|
+
|
|
347
|
+
tiktokLocationId: locations[0].location_id,
|
|
348
|
+
tiktokLocationName: locations[0].location_name, // required together with the id
|
|
349
|
+
|
|
350
|
+
tiktokCoverImageUrl: 'https://example.com/cover.jpg',
|
|
351
|
+
tiktokIsAiGenerated: false,
|
|
352
|
+
tiktokUploadToDraft: false, // true sends it to drafts and ignores the rest
|
|
353
|
+
});
|
|
298
354
|
```
|
|
299
355
|
|
|
356
|
+
### Options
|
|
357
|
+
|
|
358
|
+
| Option | Type | Notes |
|
|
359
|
+
| --- | --- | --- |
|
|
360
|
+
| `tiktokMusicId` | string | The track `id` from `getTiktokTrendingMusic()` or `searchTiktokMusic()` (not `commercial_music_id`) |
|
|
361
|
+
| `tiktokMusicVolume` | number | 0-100. Defaults to 50 when music is set |
|
|
362
|
+
| `tiktokMusicStart` | number | Music start offset in ms |
|
|
363
|
+
| `tiktokMusicEnd` | number | Music end offset in ms |
|
|
364
|
+
| `tiktokOriginalSoundVolume` | number | 0-100. Defaults to 50 when music is set, so the original audio is not muted |
|
|
365
|
+
| `tiktokLocationId` | string | `location_id` from `getTiktokLocations()` |
|
|
366
|
+
| `tiktokLocationName` | string | Required whenever `tiktokLocationId` is set |
|
|
367
|
+
| `tiktokCoverImageUrl` | string | Custom cover image URL |
|
|
368
|
+
| `tiktokIsAiGenerated` | boolean | AI-generated content disclosure |
|
|
369
|
+
| `tiktokUploadToDraft` | boolean | Publish to drafts; TikTok ignores the rest of the post settings |
|
|
370
|
+
| `tiktokPhotoCoverIndex` | number | Cover photo index for photo posts (0-based) |
|
|
371
|
+
|
|
300
372
|
## Platform-Specific Options
|
|
301
373
|
|
|
302
374
|
### TikTok (Video)
|
|
@@ -310,10 +382,29 @@ const boards = await client.getPinterestBoards('my-profile');
|
|
|
310
382
|
- `brandContentToggle` - Branded content toggle
|
|
311
383
|
- `brandOrganicToggle` - Brand organic toggle
|
|
312
384
|
|
|
385
|
+
> **Which privacy levels are available is decided by TikTok per account.** A
|
|
386
|
+
> private account, for example, is offered `FOLLOWER_OF_CREATOR`,
|
|
387
|
+
> `MUTUAL_FOLLOW_FRIENDS` and `SELF_ONLY` and has no `PUBLIC_TO_EVERYONE`.
|
|
388
|
+
> Asking for one the account does not have fails with
|
|
389
|
+
> `error_code: "tiktok_privacy_unavailable"` and an error listing the ones it
|
|
390
|
+
> does have. Omit `tiktokPrivacyLevel` on video and TikTok keeps the account's
|
|
391
|
+
> own default; on **photo** posts it defaults to `PUBLIC_TO_EVERYONE`.
|
|
392
|
+
|
|
393
|
+
See [TikTok music, location, cover and drafts](#tiktok-music-location-cover-and-drafts)
|
|
394
|
+
for those options.
|
|
395
|
+
|
|
313
396
|
### TikTok (Photos)
|
|
314
397
|
- `tiktokAutoAddMusic` - Auto add music
|
|
315
398
|
- `tiktokPhotoCoverIndex` - Index of photo for cover (0-based)
|
|
316
399
|
- `tiktokDisableComment` - Disable comments
|
|
400
|
+
- `tiktokPrivacyLevel` - PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY (same field and same per-account limits as video)
|
|
401
|
+
- `tiktokMusicId` - Commercial Music Library track id (see `getTiktokTrendingMusic`)
|
|
402
|
+
- `tiktokLocationId` / `tiktokLocationName` - Location tag, both required together
|
|
403
|
+
- `tiktokIsAiGenerated` - AI-generated content disclosure
|
|
404
|
+
|
|
405
|
+
> TikTok's photo contract takes the music track id alone: `tiktokMusicVolume`,
|
|
406
|
+
> `tiktokMusicStart`, `tiktokMusicEnd`, `tiktokOriginalSoundVolume`,
|
|
407
|
+
> `tiktokCoverImageUrl` and `tiktokUploadToDraft` are video-only.
|
|
317
408
|
|
|
318
409
|
### Instagram
|
|
319
410
|
- `instagramMediaType` - REELS, STORIES, IMAGE
|
package/index.d.ts
CHANGED
|
@@ -114,7 +114,12 @@ declare module 'upload-post' {
|
|
|
114
114
|
// ==================== TikTok Options ====================
|
|
115
115
|
|
|
116
116
|
export interface TikTokVideoOptions {
|
|
117
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Privacy setting. TikTok decides per account which levels are available (a
|
|
119
|
+
* private account has no `PUBLIC_TO_EVERYONE`); asking for another one fails
|
|
120
|
+
* with `error_code: "tiktok_privacy_unavailable"` listing the allowed ones.
|
|
121
|
+
* Omit it to keep the account's own default.
|
|
122
|
+
*/
|
|
118
123
|
tiktokPrivacyLevel?: TikTokPrivacyLevel;
|
|
119
124
|
/** Disable duet */
|
|
120
125
|
tiktokDisableDuet?: boolean;
|
|
@@ -132,6 +137,35 @@ declare module 'upload-post' {
|
|
|
132
137
|
brandContentToggle?: boolean;
|
|
133
138
|
/** Brand organic toggle */
|
|
134
139
|
brandOrganicToggle?: boolean;
|
|
140
|
+
|
|
141
|
+
// ---- Music, location, cover and draft ----
|
|
142
|
+
// Available on connections that declare the matching capability (`music`,
|
|
143
|
+
// `location`, `cover_image`, `draft`) — see `capabilities` on the TikTok
|
|
144
|
+
// account returned by listUsers() / GET /api/uploadposts/users. If your
|
|
145
|
+
// connection does not have it, the field is ignored, the post still
|
|
146
|
+
// publishes, and the response includes a per-field `warnings` entry —
|
|
147
|
+
// reconnect the TikTok account to enable it.
|
|
148
|
+
|
|
149
|
+
/** Commercial Music Library track id (see getTiktokTrendingMusic) */
|
|
150
|
+
tiktokMusicId?: string;
|
|
151
|
+
/** Music volume, 0-100. Defaults to 50 when music is set */
|
|
152
|
+
tiktokMusicVolume?: number;
|
|
153
|
+
/** Music start offset in ms */
|
|
154
|
+
tiktokMusicStart?: number;
|
|
155
|
+
/** Music end offset in ms */
|
|
156
|
+
tiktokMusicEnd?: number;
|
|
157
|
+
/** Original video audio volume, 0-100. Defaults to 50 when music is set, so the original audio is not muted */
|
|
158
|
+
tiktokOriginalSoundVolume?: number;
|
|
159
|
+
/** Location id to tag (see getTiktokLocations) */
|
|
160
|
+
tiktokLocationId?: string;
|
|
161
|
+
/** Location name. Required whenever tiktokLocationId is set */
|
|
162
|
+
tiktokLocationName?: string;
|
|
163
|
+
/** Custom cover image URL */
|
|
164
|
+
tiktokCoverImageUrl?: string;
|
|
165
|
+
/** AI-generated content disclosure */
|
|
166
|
+
tiktokIsAiGenerated?: boolean;
|
|
167
|
+
/** Publish to drafts. When true TikTok ignores the rest of the post settings */
|
|
168
|
+
tiktokUploadToDraft?: boolean;
|
|
135
169
|
}
|
|
136
170
|
|
|
137
171
|
export interface TikTokPhotoOptions {
|
|
@@ -139,16 +173,39 @@ declare module 'upload-post' {
|
|
|
139
173
|
tiktokAutoAddMusic?: boolean;
|
|
140
174
|
/** Disable comments */
|
|
141
175
|
tiktokDisableComment?: boolean;
|
|
142
|
-
/** Index of photo for cover (0-based) */
|
|
176
|
+
/** Index of photo for cover (0-based). Sent as `photo_cover_index` */
|
|
143
177
|
tiktokPhotoCoverIndex?: number;
|
|
144
|
-
/**
|
|
145
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Privacy setting. TikTok REQUIRES one on photo posts, so it defaults to
|
|
180
|
+
* `PUBLIC_TO_EVERYONE`. Which values the account may use is decided by TikTok
|
|
181
|
+
* (a private account has no `PUBLIC_TO_EVERYONE`); asking for another one
|
|
182
|
+
* fails with `error_code: "tiktok_privacy_unavailable"` listing the allowed
|
|
183
|
+
* ones.
|
|
184
|
+
*/
|
|
185
|
+
tiktokPrivacyLevel?: TikTokPrivacyLevel;
|
|
146
186
|
/** Post mode, e.g. DIRECT_POST or MEDIA_UPLOAD (inbox) */
|
|
147
187
|
tiktokPostMode?: string;
|
|
148
188
|
/** Branded content toggle */
|
|
149
189
|
brandContentToggle?: boolean;
|
|
150
190
|
/** Brand organic toggle */
|
|
151
191
|
brandOrganicToggle?: boolean;
|
|
192
|
+
|
|
193
|
+
// ---- Music, location and AI disclosure ----
|
|
194
|
+
// Photo posts accept the track id, the location pair and the AI disclosure —
|
|
195
|
+
// not the volume/trim, cover-image or draft fields, which are video-only.
|
|
196
|
+
// Available on connections that declare the matching capability (`music`,
|
|
197
|
+
// `location`) — see `capabilities` on the TikTok account returned by
|
|
198
|
+
// listUsers(). Without it the field is ignored, the post still publishes, and
|
|
199
|
+
// the response includes a per-field `warnings` entry.
|
|
200
|
+
|
|
201
|
+
/** Commercial Music Library track id (see getTiktokTrendingMusic) */
|
|
202
|
+
tiktokMusicId?: string;
|
|
203
|
+
/** Location id to tag (see getTiktokLocations) */
|
|
204
|
+
tiktokLocationId?: string;
|
|
205
|
+
/** Location name. Required whenever tiktokLocationId is set */
|
|
206
|
+
tiktokLocationName?: string;
|
|
207
|
+
/** AI-generated content disclosure */
|
|
208
|
+
tiktokIsAiGenerated?: boolean;
|
|
152
209
|
}
|
|
153
210
|
|
|
154
211
|
// ==================== Instagram Options ====================
|
|
@@ -580,6 +637,80 @@ declare module 'upload-post' {
|
|
|
580
637
|
[key: string]: any;
|
|
581
638
|
}
|
|
582
639
|
|
|
640
|
+
/** Trending window accepted by the TikTok Commercial Music Library. */
|
|
641
|
+
export type TikTokMusicDateRange = '1DAY' | '7DAY' | '30DAY' | '90DAY';
|
|
642
|
+
|
|
643
|
+
export interface TikTokMusicTrack {
|
|
644
|
+
/** Pass this as `tiktokMusicId` on an upload. */
|
|
645
|
+
id: string;
|
|
646
|
+
/**
|
|
647
|
+
* TikTok's catalogue id for the same track, returned for reference only.
|
|
648
|
+
* Do NOT send it as `tiktokMusicId`: TikTok rejects it on public posts.
|
|
649
|
+
*/
|
|
650
|
+
commercial_music_id?: string;
|
|
651
|
+
title?: string;
|
|
652
|
+
artist?: string;
|
|
653
|
+
/** Track duration in seconds. */
|
|
654
|
+
duration?: number;
|
|
655
|
+
genres?: string[];
|
|
656
|
+
/** Artwork image URL. */
|
|
657
|
+
cover_url?: string;
|
|
658
|
+
/** Audio preview URL. */
|
|
659
|
+
preview_url?: string;
|
|
660
|
+
rank?: number;
|
|
661
|
+
[key: string]: any;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
export interface TikTokTrendingMusicResponse {
|
|
665
|
+
success: boolean;
|
|
666
|
+
tracks?: TikTokMusicTrack[];
|
|
667
|
+
[key: string]: any;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
export interface TikTokMusicSearchResponse {
|
|
671
|
+
success: boolean;
|
|
672
|
+
/** Matches, ranked by relevance; same shape as the trending endpoint. */
|
|
673
|
+
tracks?: TikTokMusicTrack[];
|
|
674
|
+
/** How many tracks matched before `limit` was applied. */
|
|
675
|
+
total?: number;
|
|
676
|
+
query?: string;
|
|
677
|
+
/** What the search actually ran against. */
|
|
678
|
+
catalog?: {
|
|
679
|
+
/** Number of tracks in the searched corpus. */
|
|
680
|
+
tracks_indexed?: number;
|
|
681
|
+
/** Genre charts currently loaded for this country and period. */
|
|
682
|
+
genres_indexed?: string[];
|
|
683
|
+
/** False when this request had to load a chart from TikTok. */
|
|
684
|
+
cached?: boolean;
|
|
685
|
+
};
|
|
686
|
+
[key: string]: any;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
export interface TikTokLocation {
|
|
690
|
+
location_id: string;
|
|
691
|
+
location_name?: string;
|
|
692
|
+
location_address?: string;
|
|
693
|
+
[key: string]: any;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
export interface TikTokLocationsResponse {
|
|
697
|
+
success: boolean;
|
|
698
|
+
locations?: TikTokLocation[];
|
|
699
|
+
[key: string]: any;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
export interface TikTokPublishingSettingsResponse {
|
|
703
|
+
success: boolean;
|
|
704
|
+
/** The privacy levels THIS account may use — a subset of the four. */
|
|
705
|
+
privacy_level_options?: TikTokPrivacyLevel[];
|
|
706
|
+
/** Longest video the account can publish, in seconds. */
|
|
707
|
+
max_video_post_duration_sec?: number;
|
|
708
|
+
comment_disabled?: boolean;
|
|
709
|
+
duet_disabled?: boolean;
|
|
710
|
+
stitch_disabled?: boolean;
|
|
711
|
+
[key: string]: any;
|
|
712
|
+
}
|
|
713
|
+
|
|
583
714
|
// ==================== Client Class ====================
|
|
584
715
|
|
|
585
716
|
/**
|
|
@@ -1048,6 +1179,76 @@ declare module 'upload-post' {
|
|
|
1048
1179
|
* @param profile - Profile username
|
|
1049
1180
|
*/
|
|
1050
1181
|
getGoogleBusinessLocations(profile?: string): Promise<Record<string, unknown>>;
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* Get trending tracks from the TikTok Commercial Music Library.
|
|
1185
|
+
*
|
|
1186
|
+
* Available on connections that declare the `music` capability (see
|
|
1187
|
+
* `capabilities` on the TikTok account returned by listUsers() /
|
|
1188
|
+
* GET /api/uploadposts/users). Pass the returned `id` as `tiktokMusicId`
|
|
1189
|
+
* on an upload (NOT `commercial_music_id` — TikTok rejects that one on
|
|
1190
|
+
* public posts).
|
|
1191
|
+
*
|
|
1192
|
+
* @param profile - Profile username
|
|
1193
|
+
* @param options - Query options
|
|
1194
|
+
*/
|
|
1195
|
+
getTiktokTrendingMusic(
|
|
1196
|
+
profile: string,
|
|
1197
|
+
options?: {
|
|
1198
|
+
genre?: string;
|
|
1199
|
+
countryCode?: string;
|
|
1200
|
+
dateRange?: TikTokMusicDateRange;
|
|
1201
|
+
}
|
|
1202
|
+
): Promise<TikTokTrendingMusicResponse>;
|
|
1203
|
+
|
|
1204
|
+
/**
|
|
1205
|
+
* Search the TikTok Commercial Music Library by song title or artist.
|
|
1206
|
+
*
|
|
1207
|
+
* TikTok has no music search endpoint, so this searches the trending charts
|
|
1208
|
+
* Upload-Post caches (per genre / country / period), not TikTok's whole
|
|
1209
|
+
* catalogue. Matching is case- and accent-insensitive and every word must
|
|
1210
|
+
* match. Returns the same track objects as getTiktokTrendingMusic().
|
|
1211
|
+
*
|
|
1212
|
+
* @param profile - Profile username
|
|
1213
|
+
* @param options - Query options
|
|
1214
|
+
*/
|
|
1215
|
+
searchTiktokMusic(
|
|
1216
|
+
profile: string,
|
|
1217
|
+
options?: {
|
|
1218
|
+
q?: string;
|
|
1219
|
+
genre?: string;
|
|
1220
|
+
countryCode?: string;
|
|
1221
|
+
dateRange?: TikTokMusicDateRange;
|
|
1222
|
+
limit?: number;
|
|
1223
|
+
}
|
|
1224
|
+
): Promise<TikTokMusicSearchResponse>;
|
|
1225
|
+
|
|
1226
|
+
/**
|
|
1227
|
+
* Search TikTok locations (places) to tag on a post.
|
|
1228
|
+
*
|
|
1229
|
+
* Available on connections that declare the `location` capability (see
|
|
1230
|
+
* `capabilities` on the TikTok account returned by listUsers() /
|
|
1231
|
+
* GET /api/uploadposts/users). TikTok requires both the id and the name, so
|
|
1232
|
+
* pass `location_id` as `tiktokLocationId` and `location_name` as
|
|
1233
|
+
* `tiktokLocationName`.
|
|
1234
|
+
*
|
|
1235
|
+
* @param profile - Profile username
|
|
1236
|
+
* @param query - Search query (max 100 characters)
|
|
1237
|
+
*/
|
|
1238
|
+
getTiktokLocations(profile: string, query: string): Promise<TikTokLocationsResponse>;
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* Get what the connected TikTok account is allowed to publish.
|
|
1242
|
+
*
|
|
1243
|
+
* The point of this call is `privacy_level_options`: TikTok narrows the four
|
|
1244
|
+
* privacy values per account (a private account has no `PUBLIC_TO_EVERYONE`),
|
|
1245
|
+
* and sending one the account does not have fails the upload with
|
|
1246
|
+
* `error_code: "tiktok_privacy_unavailable"`. Ask here to offer only the
|
|
1247
|
+
* values that will work, instead of the full enum.
|
|
1248
|
+
*
|
|
1249
|
+
* @param profile - Profile username
|
|
1250
|
+
*/
|
|
1251
|
+
getTiktokPublishingSettings(profile: string): Promise<TikTokPublishingSettingsResponse>;
|
|
1051
1252
|
}
|
|
1052
1253
|
|
|
1053
1254
|
export default UploadPost;
|
package/index.js
CHANGED
|
@@ -152,25 +152,54 @@ export class UploadPost {
|
|
|
152
152
|
* @private
|
|
153
153
|
*/
|
|
154
154
|
_addTiktokParams(form, options, isVideo = true) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
// photo carousels always publish as
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
155
|
+
// [form field, option key, "flag" when the value is a boolean/number sent as
|
|
156
|
+
// soon as it is defined, "text" when an empty value means "not set"].
|
|
157
|
+
//
|
|
158
|
+
// The shared block is shared on purpose: the backend reads privacy_level and
|
|
159
|
+
// post_mode on both /upload and /upload_photos, and TikTok's photo endpoint
|
|
160
|
+
// also accepts the music track id, the location pair and is_ai_generated.
|
|
161
|
+
// Gating them behind isVideo made photo carousels always publish as
|
|
162
|
+
// PUBLIC_TO_EVERYONE / DIRECT_POST and unable to carry music or a location.
|
|
163
|
+
//
|
|
164
|
+
// The music/location/cover/draft fields need the matching capability on the
|
|
165
|
+
// connection (see `capabilities` on the TikTok account returned by
|
|
166
|
+
// listUsers() / GET /api/uploadposts/users). Without it the field is ignored,
|
|
167
|
+
// the post still publishes, and the response carries a per-field `warnings`
|
|
168
|
+
// entry.
|
|
169
|
+
const shared = [
|
|
170
|
+
['disable_comment', 'tiktokDisableComment', 'flag'],
|
|
171
|
+
['brand_content_toggle', 'brandContentToggle', 'flag'],
|
|
172
|
+
['brand_organic_toggle', 'brandOrganicToggle', 'flag'],
|
|
173
|
+
['privacy_level', 'tiktokPrivacyLevel', 'text'],
|
|
174
|
+
['post_mode', 'tiktokPostMode', 'text'],
|
|
175
|
+
['tiktok_music_id', 'tiktokMusicId', 'text'],
|
|
176
|
+
['tiktok_location_id', 'tiktokLocationId', 'text'],
|
|
177
|
+
['tiktok_location_name', 'tiktokLocationName', 'text'],
|
|
178
|
+
['tiktok_is_ai_generated', 'tiktokIsAiGenerated', 'flag'],
|
|
179
|
+
];
|
|
180
|
+
// Video-only: TikTok's photo contract takes the track id alone, with no
|
|
181
|
+
// volume or trim, and has no custom cover or draft switch.
|
|
182
|
+
const videoOnly = [
|
|
183
|
+
['disable_duet', 'tiktokDisableDuet', 'flag'],
|
|
184
|
+
['disable_stitch', 'tiktokDisableStitch', 'flag'],
|
|
185
|
+
['cover_timestamp', 'tiktokCoverTimestamp', 'flag'],
|
|
186
|
+
['is_aigc', 'tiktokIsAigc', 'flag'],
|
|
187
|
+
['tiktok_music_volume', 'tiktokMusicVolume', 'flag'],
|
|
188
|
+
['tiktok_music_start', 'tiktokMusicStart', 'flag'],
|
|
189
|
+
['tiktok_music_end', 'tiktokMusicEnd', 'flag'],
|
|
190
|
+
['tiktok_original_sound_volume', 'tiktokOriginalSoundVolume', 'flag'],
|
|
191
|
+
['tiktok_cover_image_url', 'tiktokCoverImageUrl', 'text'],
|
|
192
|
+
['tiktok_upload_to_draft', 'tiktokUploadToDraft', 'flag'],
|
|
193
|
+
];
|
|
194
|
+
const photoOnly = [
|
|
195
|
+
['auto_add_music', 'tiktokAutoAddMusic', 'flag'],
|
|
196
|
+
['photo_cover_index', 'tiktokPhotoCoverIndex', 'flag'],
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
for (const [field, key, kind] of shared.concat(isVideo ? videoOnly : photoOnly)) {
|
|
200
|
+
const value = options[key];
|
|
201
|
+
if (kind === 'flag' ? value === undefined : !value) continue;
|
|
202
|
+
form.append(field, String(value));
|
|
174
203
|
}
|
|
175
204
|
}
|
|
176
205
|
|
|
@@ -401,7 +430,7 @@ export class UploadPost {
|
|
|
401
430
|
* @param {boolean} [options.asyncUpload=true] - Process upload asynchronously
|
|
402
431
|
*
|
|
403
432
|
* TikTok options:
|
|
404
|
-
* @param {string} [options.tiktokPrivacyLevel] - PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY
|
|
433
|
+
* @param {string} [options.tiktokPrivacyLevel] - PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY. TikTok decides per account which of these are available (a private account has no PUBLIC_TO_EVERYONE); asking for another one fails with error_code tiktok_privacy_unavailable listing the allowed ones. Omit it to keep the account's own default.
|
|
405
434
|
* @param {boolean} [options.tiktokDisableDuet] - Disable duet
|
|
406
435
|
* @param {boolean} [options.tiktokDisableComment] - Disable comments
|
|
407
436
|
* @param {boolean} [options.tiktokDisableStitch] - Disable stitch
|
|
@@ -410,7 +439,24 @@ export class UploadPost {
|
|
|
410
439
|
* @param {string} [options.tiktokPostMode] - DIRECT_POST or MEDIA_UPLOAD
|
|
411
440
|
* @param {boolean} [options.brandContentToggle] - Branded content toggle
|
|
412
441
|
* @param {boolean} [options.brandOrganicToggle] - Brand organic toggle
|
|
413
|
-
*
|
|
442
|
+
*
|
|
443
|
+
* TikTok music, location, cover and draft options. Available on connections that
|
|
444
|
+
* declare the matching `capabilities` (music, location, cover_image, draft) on the
|
|
445
|
+
* TikTok account returned by listUsers() / GET /api/uploadposts/users. If your
|
|
446
|
+
* connection does not have it, the field is ignored, the post still publishes, and
|
|
447
|
+
* the response includes a per-field `warnings` entry — reconnect the TikTok account
|
|
448
|
+
* to enable it.
|
|
449
|
+
* @param {string} [options.tiktokMusicId] - Commercial Music Library track id (see getTiktokTrendingMusic)
|
|
450
|
+
* @param {number} [options.tiktokMusicVolume] - Music volume 0-100 (defaults to 50 when music is set)
|
|
451
|
+
* @param {number} [options.tiktokMusicStart] - Music start offset in ms
|
|
452
|
+
* @param {number} [options.tiktokMusicEnd] - Music end offset in ms
|
|
453
|
+
* @param {number} [options.tiktokOriginalSoundVolume] - Original video audio volume 0-100 (defaults to 50 when music is set, so the original audio is not muted)
|
|
454
|
+
* @param {string} [options.tiktokLocationId] - Location id (see getTiktokLocations)
|
|
455
|
+
* @param {string} [options.tiktokLocationName] - Location name, required whenever tiktokLocationId is set
|
|
456
|
+
* @param {string} [options.tiktokCoverImageUrl] - Custom cover image URL
|
|
457
|
+
* @param {boolean} [options.tiktokIsAiGenerated] - AI-generated content disclosure
|
|
458
|
+
* @param {boolean} [options.tiktokUploadToDraft] - Publish to drafts. When true TikTok ignores the rest of the post settings
|
|
459
|
+
*
|
|
414
460
|
* Instagram options:
|
|
415
461
|
* @param {string} [options.instagramMediaType] - REELS or STORIES
|
|
416
462
|
* @param {boolean} [options.instagramShareToFeed] - Share to feed
|
|
@@ -521,10 +567,22 @@ export class UploadPost {
|
|
|
521
567
|
* TikTok options:
|
|
522
568
|
* @param {boolean} [options.tiktokAutoAddMusic] - Auto add music
|
|
523
569
|
* @param {boolean} [options.tiktokDisableComment] - Disable comments
|
|
524
|
-
* @param {number} [options.tiktokPhotoCoverIndex] - Index of photo for cover (0-based)
|
|
570
|
+
* @param {number} [options.tiktokPhotoCoverIndex] - Index of photo for cover (0-based). Sent as `photo_cover_index`
|
|
571
|
+
* @param {string} [options.tiktokPrivacyLevel] - PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY. TikTok requires one on photo posts, so it defaults to PUBLIC_TO_EVERYONE; asking for one the account does not have fails with error_code tiktok_privacy_unavailable listing the allowed ones.
|
|
525
572
|
* @param {boolean} [options.brandContentToggle] - Branded content toggle
|
|
526
573
|
* @param {boolean} [options.brandOrganicToggle] - Brand organic toggle
|
|
527
|
-
*
|
|
574
|
+
*
|
|
575
|
+
* TikTok music, location and AI disclosure on photo posts. TikTok's photo contract
|
|
576
|
+
* takes the track id alone — no volume/trim, no custom cover, no draft (those are
|
|
577
|
+
* video-only). Available on connections that declare the matching `capabilities`
|
|
578
|
+
* (music, location) on the TikTok account returned by listUsers(); otherwise the
|
|
579
|
+
* field is ignored, the post still publishes, and the response includes a
|
|
580
|
+
* per-field `warnings` entry.
|
|
581
|
+
* @param {string} [options.tiktokMusicId] - Commercial Music Library track id (see getTiktokTrendingMusic)
|
|
582
|
+
* @param {string} [options.tiktokLocationId] - Location id (see getTiktokLocations)
|
|
583
|
+
* @param {string} [options.tiktokLocationName] - Location name, required whenever tiktokLocationId is set
|
|
584
|
+
* @param {boolean} [options.tiktokIsAiGenerated] - AI-generated content disclosure
|
|
585
|
+
*
|
|
528
586
|
* Instagram options:
|
|
529
587
|
* @param {string} [options.instagramMediaType] - IMAGE or STORIES
|
|
530
588
|
* @param {string} [options.instagramCollaborators] - Comma-separated collaborator usernames
|
|
@@ -1242,6 +1300,102 @@ export class UploadPost {
|
|
|
1242
1300
|
return this._request('/uploadposts/pinterest/boards', 'GET', params);
|
|
1243
1301
|
}
|
|
1244
1302
|
|
|
1303
|
+
/**
|
|
1304
|
+
* Catalogue filters shared by the trending and search endpoints, so both
|
|
1305
|
+
* address the same cached chart slice.
|
|
1306
|
+
* @private
|
|
1307
|
+
*/
|
|
1308
|
+
_tiktokMusicParams(profile, options = {}) {
|
|
1309
|
+
const params = { profile };
|
|
1310
|
+
if (options.genre) params.genre = options.genre;
|
|
1311
|
+
if (options.countryCode) params.country_code = options.countryCode;
|
|
1312
|
+
if (options.dateRange) params.date_range = options.dateRange;
|
|
1313
|
+
return params;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
/**
|
|
1317
|
+
* Get trending tracks from the TikTok Commercial Music Library.
|
|
1318
|
+
*
|
|
1319
|
+
* Available on connections that declare the `music` capability (see
|
|
1320
|
+
* `capabilities` on the TikTok account returned by listUsers() /
|
|
1321
|
+
* GET /api/uploadposts/users). The returned `id` is what you pass as
|
|
1322
|
+
* `tiktokMusicId` on an upload — not `commercial_music_id`, which TikTok
|
|
1323
|
+
* rejects on public posts.
|
|
1324
|
+
*
|
|
1325
|
+
* @param {string} profile - Profile username
|
|
1326
|
+
* @param {Object} [options] - Query options
|
|
1327
|
+
* @param {string} [options.genre] - Genre filter (e.g. ALL, POP). Defaults to ALL upstream.
|
|
1328
|
+
* @param {string} [options.countryCode] - ISO country code. Defaults to US upstream.
|
|
1329
|
+
* @param {('1DAY'|'7DAY'|'30DAY'|'90DAY')} [options.dateRange] - Trending window. Defaults to 7DAY upstream.
|
|
1330
|
+
* @returns {Promise<Object>} Trending tracks
|
|
1331
|
+
*/
|
|
1332
|
+
async getTiktokTrendingMusic(profile, options = {}) {
|
|
1333
|
+
return this._request('/uploadposts/tiktok/music/trending', 'GET',
|
|
1334
|
+
this._tiktokMusicParams(profile, options));
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Search the TikTok Commercial Music Library by song title or artist.
|
|
1339
|
+
*
|
|
1340
|
+
* TikTok itself has no music search endpoint — its only catalogue read is the
|
|
1341
|
+
* trending chart for a genre/country/period. Upload-Post caches those charts
|
|
1342
|
+
* and matches your text against them, so this searches the trending charts,
|
|
1343
|
+
* not TikTok's whole catalogue. Matching is case- and accent-insensitive and
|
|
1344
|
+
* every word must match.
|
|
1345
|
+
*
|
|
1346
|
+
* Returns the same track objects as getTiktokTrendingMusic(), so the `id` is
|
|
1347
|
+
* again what you pass as `tiktokMusicId` on an upload.
|
|
1348
|
+
*
|
|
1349
|
+
* @param {string} profile - Profile username
|
|
1350
|
+
* @param {Object} [options] - Query options
|
|
1351
|
+
* @param {string} [options.q] - Text matched against titles and artists (max 80 chars). Omit for the chart in trending order.
|
|
1352
|
+
* @param {string} [options.genre] - Genre filter (e.g. ALL, POP). Defaults to ALL upstream.
|
|
1353
|
+
* @param {string} [options.countryCode] - ISO country code choosing which chart is searched. Defaults to US upstream.
|
|
1354
|
+
* @param {('1DAY'|'7DAY'|'30DAY'|'90DAY')} [options.dateRange] - Chart window. Defaults to 7DAY upstream.
|
|
1355
|
+
* @param {number} [options.limit] - Maximum tracks to return (capped at 100 upstream).
|
|
1356
|
+
* @returns {Promise<Object>} Matching tracks plus a `catalog` block describing the corpus searched
|
|
1357
|
+
*/
|
|
1358
|
+
async searchTiktokMusic(profile, options = {}) {
|
|
1359
|
+
const params = this._tiktokMusicParams(profile, options);
|
|
1360
|
+
if (options.q) params.q = options.q;
|
|
1361
|
+
if (options.limit !== undefined && options.limit !== null) params.limit = options.limit;
|
|
1362
|
+
return this._request('/uploadposts/tiktok/music/search', 'GET', params);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* Search TikTok locations (places) to tag on a post.
|
|
1367
|
+
*
|
|
1368
|
+
* Available on connections that declare the `location` capability (see
|
|
1369
|
+
* `capabilities` on the TikTok account returned by listUsers() /
|
|
1370
|
+
* GET /api/uploadposts/users). Pass the returned `location_id` as
|
|
1371
|
+
* `tiktokLocationId` and `location_name` as `tiktokLocationName` on an upload;
|
|
1372
|
+
* TikTok requires both together.
|
|
1373
|
+
*
|
|
1374
|
+
* @param {string} profile - Profile username
|
|
1375
|
+
* @param {string} query - Search query (max 100 characters)
|
|
1376
|
+
* @returns {Promise<Object>} Matching locations (up to 20)
|
|
1377
|
+
*/
|
|
1378
|
+
async getTiktokLocations(profile, query) {
|
|
1379
|
+
return this._request('/uploadposts/tiktok/locations', 'GET', { profile, q: query });
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
* Get what the connected TikTok account is allowed to publish.
|
|
1384
|
+
*
|
|
1385
|
+
* The point of this call is `privacy_level_options`: TikTok narrows the four
|
|
1386
|
+
* privacy values per account (a private account has no `PUBLIC_TO_EVERYONE`),
|
|
1387
|
+
* and sending one the account does not have fails the upload with
|
|
1388
|
+
* `error_code: "tiktok_privacy_unavailable"`. Ask here to offer only the
|
|
1389
|
+
* values that will work, instead of the full enum.
|
|
1390
|
+
*
|
|
1391
|
+
* @param {string} profile - Profile username
|
|
1392
|
+
* @returns {Promise<Object>} privacy_level_options, max_video_post_duration_sec
|
|
1393
|
+
* and the account's comment/duet/stitch switches
|
|
1394
|
+
*/
|
|
1395
|
+
async getTiktokPublishingSettings(profile) {
|
|
1396
|
+
return this._request('/uploadposts/tiktok/settings', 'GET', { profile });
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1245
1399
|
/**
|
|
1246
1400
|
* Get Google Business Profile locations for a connected account
|
|
1247
1401
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "upload-post",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "Official client library for Upload-Post API - Cross-platform social media upload for TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, and X (Twitter)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|