78 lines
3.5 KiB
TypeScript
78 lines
3.5 KiB
TypeScript
import type { BrowserContext } from 'playwright';
|
||
import { uploadFile, generateUniqueFileName } from '@/lib/minio';
|
||
import { downloadBinary } from './network';
|
||
import { pickFirstUrl } from './utils';
|
||
|
||
/**
|
||
* 下载头像并上传到 MinIO,返回外链;失败时回退为原始链接。
|
||
*/
|
||
export async function uploadAvatarFromUrl(
|
||
context: BrowserContext,
|
||
srcUrl?: string | null,
|
||
nameHint?: string
|
||
): Promise<string | undefined> {
|
||
if (!srcUrl) return undefined;
|
||
try {
|
||
const { buffer, contentType, ext } = await downloadBinary(context, srcUrl);
|
||
const safeExt = ext || 'jpg';
|
||
const baseName = nameHint ? `${nameHint}.${safeExt}` : `avatar.${safeExt}`;
|
||
const fileName = generateUniqueFileName(baseName, 'douyin/avatars');
|
||
const uploaded = await uploadFile(buffer, fileName, { 'Content-Type': contentType });
|
||
return uploaded;
|
||
} catch (e) {
|
||
console.warn('[avatar] 上传失败,使用原始链接:', (e as Error)?.message || e);
|
||
return srcUrl || undefined;
|
||
}
|
||
}
|
||
|
||
/** 下载图文作品的图片和音乐并上传到 MinIO */
|
||
export async function handleImagePost(
|
||
context: BrowserContext,
|
||
aweme: DouyinImageAweme
|
||
): Promise<{ images: { url: string; width?: number; height?: number }[]; musicUrl?: string }> {
|
||
const awemeId = aweme.aweme_id;
|
||
const uploadedImages: { url: string; width?: number; height?: number, video?: string }[] = [];
|
||
|
||
// 下载图片(顺序保持)
|
||
for (let i = 0; i < (aweme.images?.length || 0); i++) {
|
||
const img = aweme.images[i];
|
||
const url = pickFirstUrl(img?.url_list);
|
||
if (!url) continue;
|
||
const { buffer, contentType, ext } = await downloadBinary(context, url);
|
||
const safeExt = ext || 'jpg';
|
||
const fileName = generateUniqueFileName(`${awemeId}/${i}.${safeExt}`, 'douyin/images');
|
||
const uploaded = await uploadFile(buffer, fileName, { 'Content-Type': contentType });
|
||
|
||
if (img.video?.play_addr) {
|
||
// 如果是动图,下载 video 并上传
|
||
const videoUrl = img.video.play_addr[0]?.src;
|
||
if (videoUrl) {
|
||
try {
|
||
const { buffer: videoBuffer, contentType: videoContentType, ext: videoExt } = await downloadBinary(context, videoUrl);
|
||
const safeVideoExt = videoExt || 'mp4';
|
||
const videoFileName = generateUniqueFileName(`${awemeId}/${i}_animated.${safeVideoExt}`, 'douyin/images');
|
||
const uploadedVideo = await uploadFile(videoBuffer, videoFileName, { 'Content-Type': videoContentType });
|
||
// 将动图的 video URL 也存储起来
|
||
uploadedImages.push({ url: uploaded, width: img?.width, height: img?.height, video: uploadedVideo });
|
||
} catch (e) {
|
||
console.warn(`[image] 动图视频上传失败,跳过:`, (e as Error)?.message || e);
|
||
}
|
||
}
|
||
} else {
|
||
uploadedImages.push({ url: uploaded, width: img?.width, height: img?.height });
|
||
}
|
||
}
|
||
|
||
// 下载音乐(可选)
|
||
let musicUrl: string | undefined;
|
||
const audioSrc = pickFirstUrl(aweme.music?.play_url?.url_list);
|
||
if (audioSrc) {
|
||
const { buffer, contentType, ext } = await downloadBinary(context, audioSrc);
|
||
const safeExt = ext || 'mp3';
|
||
const fileName = generateUniqueFileName(`${awemeId}.${safeExt}`, 'douyin/audios');
|
||
musicUrl = await uploadFile(buffer, fileName, { 'Content-Type': contentType });
|
||
}
|
||
|
||
return { images: uploadedImages, musicUrl };
|
||
}
|