test-douyin-real-upload.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /**
  2. * 抖音视频上传真实测试
  3. *
  4. * 实际尝试上传一个视频文件到抖音创作者中心
  5. */
  6. const axios = require('axios');
  7. const fs = require('fs');
  8. const path = require('path');
  9. const crypto = require('crypto');
  10. // 你的 Cookie
  11. const TEST_COOKIE = 'x-web-secsdk-uid=dad3a1e2-8a04-479e-b2f7-23e5fb2d3886; gfkadpd=2906,33638; _tea_utm_cache_2906=undefined; csrf_session_id=62637711d69c21ff4bf1659c19031660; bd_ticket_guard_client_web_domain=2; passport_csrf_token=7192948c206b4372d66f598cea1d7c61; passport_csrf_token_default=7192948c206b4372d66f598cea1d7c61; biz_trace_id=373e3792; s_v_web_id=verify_mob7d51c_x0sF5nei_Cco9_4BUu_98sZ_aFkNzoENvP5B; passport_mfa_token=CjflZ3s7mnwLbkofuNtTzy2RsWexDVR6K%2BsIK5%2F25wtrZUHKKJlKLB0odxhfRTaTZZzqnjn%2BRfzNGkoKPAAAAAAAAAAAAABQVsh9LUcYeGk2iu325F3Bec5Pp5ESQKauA5qdZBsXdV7z3GHwkN6zD%2FJMviqOn2GmJxDvyo8OGPax0WwgAiIBAy3dXEI%3D; d_ticket=155055d9db38f2263ee71e2fc00d49f77a031; passport_assist_user=CkF5DKWV1qhlmpMHAtyVnDRmURdZiRjWmCWTEfn4TuROQNv4kdp7TSS47uow1Dmp1gPZyHMpfPkDyhAMMMpTQHiOBBpKCjwAAAAAAAAAAAAAUFbsnhX8yQaNnoGrvUaWJnu6yz67aJ1Utu-MPkDIrHOKT5VI7dMZsDd0j5hnJePaJlUQ78qPDhiJr9ZUIAEiAQMHOYLY; n_mh=NZ66y_3jbT04s2gefO-EMKEB6EsWtjVtv18mMDhVYNc; passport_auth_status=fdb4f4d164ad11b102e78f3885a24dec%2C; passport_auth_status_ss=fdb4f4d164ad11b102e78f3885a24dec%2C; sid_guard=e4f165b3ec9c0c2dd1cf045e27f416e1%7C1776931919%7C5184000%7CMon%2C+22-Jun-2026+08%3A11%3A59+GMT; uid_tt=2bab11562b398876659ba823f98b1f3e; uid_tt_ss=2bab11562b398876659ba823f98b1f3e; sid_tt=e4f165b3ec9c0c2dd1cf045e27f416e1; sessionid=e4f165b3ec9c0c2dd1cf045e27f416e1; sessionid_ss=e4f165b3ec9c0c2dd1cf045e27f416e1; session_tlb_tag=sttt%7C9%7C5PFls-ycDC3RzwReJ_QW4f_________hTYyTymF9qfwYeiMCWpKD1Eb7EiLJtnpA63x9e2YieXo%3D; is_staff_user=false; has_biz_token=false; sid_ucp_v1=1.0.0-KGM0MzY5MjE2ZDQwZDA3NGI1YTQ1MzUxNWRlOWE4MmMwZWQ5Y2QyNTYKIQi-z9Dv0PT1BRDPqKfPBhjaFiAMMObBw_IFOAJA8QdIBBoCbGYiIGU0ZjE2NWIzZWM5YzBjMmRkMWNmMDQ1ZTI3ZjQxNmUx; ssid_ucp_v1=1.0.0-KGM0MzY5MjE2ZDQwZDA3NGI1YTQ1MzUxNWRlOWE4MmMwZWQ5Y2QyNTYKIQi-z9Dv0PT1BRDPqKfPBhjaFiAMMObBw_IFOAJA8QdIBBoCbGYiIGU0ZjE2NWIzZWM5YzBjMmRkMWNmMDQ1ZTI3ZjQxNmUx; _bd_ticket_crypt_doamin=2; _bd_ticket_crypt_cookie=c6e1b35622e0600393a280031b3357a6; __security_mc_1_s_sdk_sign_data_key_web_protect=309e8ad3-49f7-afe1; __security_mc_1_s_sdk_cert_key=490e3cb6-413b-acb2; __security_mc_1_s_sdk_crypt_sdk=157a90f6-4caa-bd08; __security_server_data_status=1; _tea_utm_cache_1128=undefined; passport_fe_beating_status=false; odin_tt=f2253724dbefb92fbe13ef836a6fa43190a0683ba691cd11fe34a8d9c134af4e975e070ea0837822f0348987d6aa95e4ac389fd0b0d713e3dc52e56be6f0cf3d; gd_random=eyJtYXRjaCI6ZmFsc2UsInBlcmNlbnQiOjAuNzQzNTcxODA1MjAxNjQ3MX0=.4bIx4ulWPDUSOUwlmd5J+dXI/dVZDHUPyh178R9Cpm4=; ttwid=1%7CdW6G4pyoNaDJS48gj7TFfY8z_TFJDxxPIfB3TDR3JNI%7C1776932357%7C1ce0dc2ab274c18cb8ffb707c7564c9dd029e72560cfaca33a7e2cbb8e26da86; bd_ticket_guard_client_data=eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWl0ZXJhdGlvbi12ZXJzaW9uIjoxLCJiZC10aWNrZXQtZ3VhcmQtcmVlLXB1YmxpYy1rZXkiOiJCRmVlZWJGWEx1ZGRlanpRSjZGRDAxZ2M1UGxvSlNLR1dsTk5mMjU0RmtlZXVsYVZYQ25wbm5STTdsYXVVT0YzQlJZUUhQM2hsSTQ5WE5lSldkdEcrdm89IiwiYmQtdGlja2V0LWd1YXJkLXdlYi12ZXJzaW9uIjoyfQ%3D%3D';
  12. const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36';
  13. // 测试视频文件路径
  14. const VIDEO_PATH = path.join(__dirname, 'public', 'videos', 'test_video.mp4');
  15. // 基础请求头
  16. function getBaseHeaders() {
  17. return {
  18. 'accept': 'application/json, text/plain, */*',
  19. 'accept-language': 'zh-CN,zh;q=0.9',
  20. 'Cookie': TEST_COOKIE,
  21. 'referer': 'https://creator.douyin.com/creator-micro/content/publish',
  22. 'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
  23. 'sec-ch-ua-mobile': '?0',
  24. 'sec-ch-ua-platform': '"Windows"',
  25. 'sec-fetch-dest': 'empty',
  26. 'sec-fetch-mode': 'cors',
  27. 'sec-fetch-site': 'same-origin',
  28. 'user-agent': USER_AGENT,
  29. };
  30. }
  31. // 计算文件 MD5
  32. async function calculateMD5(filePath) {
  33. return new Promise((resolve, reject) => {
  34. const hash = crypto.createHash('md5');
  35. const stream = fs.createReadStream(filePath);
  36. stream.on('data', (data) => hash.update(data));
  37. stream.on('end', () => resolve(hash.digest('hex')));
  38. stream.on('error', reject);
  39. });
  40. }
  41. async function testUpload() {
  42. console.log('🚀 开始抖音视频上传测试\n');
  43. // 检查视频文件
  44. if (!fs.existsSync(VIDEO_PATH)) {
  45. console.error('❌ 视频文件不存在:', VIDEO_PATH);
  46. return;
  47. }
  48. const videoStats = fs.statSync(VIDEO_PATH);
  49. console.log('📹 视频文件信息:');
  50. console.log(' 路径:', VIDEO_PATH);
  51. console.log(' 大小:', (videoStats.size / 1024 / 1024).toFixed(2), 'MB');
  52. console.log('');
  53. try {
  54. // Step 1: 计算视频 MD5
  55. console.log('Step 1: 计算视频 MD5...');
  56. const videoMD5 = await calculateMD5(VIDEO_PATH);
  57. console.log('✅ MD5:', videoMD5);
  58. console.log('');
  59. // Step 2: 获取上传凭证
  60. console.log('Step 2: 请求上传凭证...');
  61. const initResponse = await axios.post(
  62. 'https://creator.douyin.com/web/api/v1/media/upload/video/permission/',
  63. null,
  64. {
  65. headers: {
  66. ...getBaseHeaders(),
  67. 'content-type': 'application/json',
  68. },
  69. params: {
  70. video_md5: videoMD5,
  71. video_size: videoStats.size,
  72. },
  73. timeout: 10000,
  74. }
  75. );
  76. console.log('✅ 上传凭证响应:', JSON.stringify(initResponse.data, null, 2));
  77. console.log('');
  78. // Step 3: 上传视频文件
  79. console.log('Step 3: 上传视频文件...');
  80. const videoBuffer = fs.readFileSync(VIDEO_PATH);
  81. const FormData = require('form-data');
  82. const formData = new FormData();
  83. formData.append('video', videoBuffer, {
  84. filename: 'test_video.mp4',
  85. contentType: 'video/mp4',
  86. });
  87. const uploadResponse = await axios.post(
  88. 'https://creator.douyin.com/web/api/v1/media/upload/video/',
  89. formData,
  90. {
  91. headers: {
  92. ...getBaseHeaders(),
  93. ...formData.getHeaders(),
  94. },
  95. params: {
  96. video_md5: videoMD5,
  97. },
  98. timeout: 60000,
  99. maxContentLength: Infinity,
  100. maxBodyLength: Infinity,
  101. }
  102. );
  103. console.log('✅ 上传成功!');
  104. console.log('响应:', JSON.stringify(uploadResponse.data, null, 2));
  105. console.log('');
  106. // Step 4: 创建作品(发布)
  107. console.log('Step 4: 创建作品...');
  108. const createResponse = await axios.post(
  109. 'https://creator.douyin.com/web/api/v1/media/video/create/',
  110. {
  111. video_id: uploadResponse.data.video_id,
  112. title: '测试视频 - 自动化上传',
  113. description: '这是一个测试视频,用于验证自动上传功能',
  114. },
  115. {
  116. headers: {
  117. ...getBaseHeaders(),
  118. 'content-type': 'application/json',
  119. },
  120. timeout: 10000,
  121. }
  122. );
  123. console.log('✅ 作品创建成功!');
  124. console.log('响应:', JSON.stringify(createResponse.data, null, 2));
  125. console.log('');
  126. console.log('🎉 完整流程测试成功!');
  127. } catch (error) {
  128. console.error('\n❌ 上传失败');
  129. console.error('错误阶段:', error.config?.url);
  130. console.error('状态码:', error.response?.status);
  131. console.error('错误信息:', error.response?.data || error.message);
  132. if (error.response?.data) {
  133. console.error('详细响应:', JSON.stringify(error.response.data, null, 2));
  134. }
  135. // 分析错误
  136. if (error.response?.status === 403) {
  137. console.log('\n💡 可能原因:');
  138. console.log(' 1. Cookie 已过期');
  139. console.log(' 2. 缺少 CSRF Token');
  140. console.log(' 3. 需要浏览器指纹验证');
  141. } else if (error.response?.status === 400) {
  142. console.log('\n💡 可能原因:');
  143. console.log(' 1. 参数格式不正确');
  144. console.log(' 2. 视频格式不支持');
  145. console.log(' 3. 视频大小超出限制');
  146. }
  147. }
  148. }
  149. // 运行测试
  150. testUpload();