TikTok V6

This commit is contained in:
2026-07-11 22:34:31 +02:00
parent 438b3dff7b
commit 3dfed67539

View File

@@ -1225,7 +1225,7 @@ app.get('/api/tiktok/connect', (request, response) => {
const authUrl = new URL('https://www.tiktok.com/v2/auth/authorize/'); const authUrl = new URL('https://www.tiktok.com/v2/auth/authorize/');
authUrl.searchParams.set('client_key', clientKey); authUrl.searchParams.set('client_key', clientKey);
authUrl.searchParams.set('scope', 'user.info.basic,user.info.stats,video.publish,video.upload,video.list'); authUrl.searchParams.set('scope', 'user.info.basic,video.publish,video.upload');
authUrl.searchParams.set('response_type', 'code'); authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('redirect_uri', redirectUri); authUrl.searchParams.set('redirect_uri', redirectUri);
authUrl.searchParams.set('state', oauthState); authUrl.searchParams.set('state', oauthState);
@@ -1571,11 +1571,12 @@ app.get('/api/tiktok/upload/status', async (request, response) => {
}); });
// ─── TikTok Analytics ─────────────────────────────────────────────────────── // ─── TikTok Analytics ───────────────────────────────────────────────────────
// Live read-only stats from the Display API. Requires the user.info.stats and // user.info only requests fields covered by the approved user.info.basic
// video.list scopes — accounts connected before the scope change must // scope. video.list needs a scope GreenLens's TikTok app isn't approved for
// re-authorize via /api/tiktok/connect. // (would require adding the Display API product and a new app review), so
// video stats are omitted until that's added.
const TIKTOK_USER_FIELDS = 'display_name,follower_count,following_count,likes_count,video_count'; const TIKTOK_USER_FIELDS = 'display_name,avatar_url';
const TIKTOK_VIDEO_FIELDS = 'id,title,video_description,duration,create_time,share_url,view_count,like_count,comment_count,share_count'; const TIKTOK_VIDEO_FIELDS = 'id,title,video_description,duration,create_time,share_url,view_count,like_count,comment_count,share_count';
const TIKTOK_VIDEO_PAGE_SIZE = 20; // Display API maximum per page const TIKTOK_VIDEO_PAGE_SIZE = 20; // Display API maximum per page
@@ -1634,6 +1635,8 @@ app.get('/api/tiktok/analytics', async (request, response) => {
); );
const videos = []; const videos = [];
let videoListUnavailable = false;
try {
let cursor; let cursor;
let hasMore = true; let hasMore = true;
while (hasMore && videos.length < maxVideos) { while (hasMore && videos.length < maxVideos) {
@@ -1650,6 +1653,15 @@ app.get('/api/tiktok/analytics', async (request, response) => {
cursor = page?.data?.cursor; cursor = page?.data?.cursor;
hasMore = Boolean(page?.data?.has_more) && pageVideos.length > 0; hasMore = Boolean(page?.data?.has_more) && pageVideos.length > 0;
} }
} catch (error) {
// video.list is not part of GreenLens's approved TikTok app scopes yet;
// still return the confirmed account identity from user.info.
if (error.body?.error?.code === 'scope_not_authorized') {
videoListUnavailable = true;
} else {
throw error;
}
}
const { videos: enrichedVideos, summary } = summarizeTiktokVideos(videos); const { videos: enrichedVideos, summary } = summarizeTiktokVideos(videos);
@@ -1657,6 +1669,7 @@ app.get('/api/tiktok/analytics', async (request, response) => {
user: userResult?.data?.user || null, user: userResult?.data?.user || null,
summary, summary,
videos: enrichedVideos, videos: enrichedVideos,
videoListUnavailable,
}); });
} catch (error) { } catch (error) {
const payload = toApiErrorPayload(error); const payload = toApiErrorPayload(error);