9b395f5cc3
E2E Headed Chrome / e2e-headed (macos-15) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (ubuntu-latest) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (windows-latest) (push) Has been cancelled
CI / build (macos-latest) (push) Has been cancelled
CI / build (ubuntu-latest) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / bun-test (push) Has been cancelled
CI / adapter-test (push) Has been cancelled
CI / smoke-test (macos-latest) (push) Has been cancelled
CI / smoke-test (ubuntu-latest) (push) Has been cancelled
Security Audit / audit (push) Has been cancelled
Build Chrome Extension / build (push) Has been cancelled
Trigger Website Rebuild (Docs Updated) / dispatch (push) Has been cancelled
54 lines
1.8 KiB
JavaScript
54 lines
1.8 KiB
JavaScript
/**
|
|
* ImageX cover image uploader.
|
|
*
|
|
* Uploads a JPEG/PNG image to ByteDance ImageX via a pre-signed PUT URL
|
|
* obtained from the Douyin "apply cover upload" API.
|
|
*/
|
|
import * as fs from 'node:fs';
|
|
import * as path from 'node:path';
|
|
import { CommandExecutionError } from '@jackwener/opencli/errors';
|
|
/**
|
|
* Detect MIME type from file extension.
|
|
* Falls back to image/jpeg for unknown extensions.
|
|
*/
|
|
function detectContentType(filePath) {
|
|
const ext = path.extname(filePath).toLowerCase();
|
|
switch (ext) {
|
|
case '.png':
|
|
return 'image/png';
|
|
case '.gif':
|
|
return 'image/gif';
|
|
case '.webp':
|
|
return 'image/webp';
|
|
default:
|
|
return 'image/jpeg';
|
|
}
|
|
}
|
|
/**
|
|
* Upload a cover image to ByteDance ImageX via a pre-signed PUT URL.
|
|
*
|
|
* @param imagePath - Local file path to the image (JPEG/PNG/etc.)
|
|
* @param uploadInfo - Upload URL and store_uri from the apply cover upload API
|
|
* @returns The store_uri (= image_uri for use in create_v2)
|
|
*/
|
|
export async function imagexUpload(imagePath, uploadInfo) {
|
|
if (!fs.existsSync(imagePath)) {
|
|
throw new CommandExecutionError(`Cover image file not found: ${imagePath}`, 'Ensure the file path is correct and accessible.');
|
|
}
|
|
const imageBuffer = fs.readFileSync(imagePath);
|
|
const contentType = detectContentType(imagePath);
|
|
const res = await fetch(uploadInfo.upload_url, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': contentType,
|
|
'Content-Length': String(imageBuffer.byteLength),
|
|
},
|
|
body: imageBuffer,
|
|
});
|
|
if (!res.ok) {
|
|
const body = await res.text().catch(() => '');
|
|
throw new CommandExecutionError(`ImageX upload failed with status ${res.status}: ${body}`, 'Check that the upload URL is valid and has not expired.');
|
|
}
|
|
return uploadInfo.store_uri;
|
|
}
|