/ clis / douyin / _shared / transcode.js
transcode.js
 1  /**
 2   * Transcode poller for Douyin video processing.
 3   *
 4   * After a video is uploaded via TOS and the "confirm upload" API is called,
 5   * Douyin transcodes the video asynchronously. This module polls the transcode
 6   * status endpoint until encode=2 (complete) or a timeout is reached.
 7   */
 8  import { TimeoutError } from '@jackwener/opencli/errors';
 9  import { browserFetch } from './browser-fetch.js';
10  const POLL_INTERVAL_MS = 3_000;
11  const DEFAULT_TIMEOUT_MS = 300_000;
12  const TRANSCODE_URL_BASE = 'https://creator.douyin.com/web/api/media/video/transend/';
13  /**
14   * Lower-level poll function that accepts an injected fetch function.
15   * Exported for testability.
16   */
17  export async function pollTranscodeWithFetch(fetchFn, page, videoId, timeoutMs = DEFAULT_TIMEOUT_MS) {
18      const url = `${TRANSCODE_URL_BASE}?video_id=${encodeURIComponent(videoId)}&aid=1128`;
19      const deadline = Date.now() + timeoutMs;
20      while (Date.now() < deadline) {
21          const result = (await fetchFn(page, 'GET', url));
22          if (result.encode === 2) {
23              return result;
24          }
25          // Wait before next poll, but don't exceed the deadline
26          const remaining = deadline - Date.now();
27          if (remaining <= 0)
28              break;
29          await new Promise(resolve => setTimeout(resolve, Math.min(POLL_INTERVAL_MS, remaining)));
30      }
31      throw new TimeoutError(`Douyin transcode for video ${videoId}`, Math.round(timeoutMs / 1000));
32  }
33  /**
34   * Poll Douyin's transcode status endpoint until the video is fully transcoded
35   * (encode=2) or the timeout expires.
36   *
37   * @param page - Browser page for making credentialed API calls
38   * @param videoId - The video_id returned from the confirm upload step
39   * @param timeoutMs - Maximum wait time in ms (default: 300 000 = 5 minutes)
40   * @returns TranscodeResult including duration, fps, dimensions, and poster info
41   * @throws TimeoutError if transcode does not complete within timeoutMs
42   */
43  export async function pollTranscode(page, videoId, timeoutMs) {
44      return pollTranscodeWithFetch(browserFetch, page, videoId, timeoutMs);
45  }