312 lines
9.1 KiB
JavaScript
312 lines
9.1 KiB
JavaScript
/**
|
|
* Auto Upload Service
|
|
*
|
|
* Beginner/Intermediate 레벨 사용자의 영상 자동 업로드를 처리합니다.
|
|
* - YouTube, Instagram, TikTok에 자동 업로드
|
|
* - SEO 데이터 자동 생성 (Gemini 사용)
|
|
*/
|
|
|
|
const db = require('../db');
|
|
const path = require('path');
|
|
|
|
/**
|
|
* 사용자 레벨에 따른 자동 업로드 설정 확인
|
|
*/
|
|
const shouldAutoUpload = async (userId) => {
|
|
return new Promise((resolve, reject) => {
|
|
const query = `
|
|
SELECT u.experience_level, ags.auto_youtube, ags.auto_instagram, ags.auto_tiktok
|
|
FROM users u
|
|
LEFT JOIN auto_generation_settings ags ON u.id = ags.user_id
|
|
WHERE u.id = ?
|
|
`;
|
|
|
|
db.get(query, [userId], (err, row) => {
|
|
if (err) {
|
|
console.error('자동 업로드 설정 조회 실패:', err);
|
|
return resolve({ shouldUpload: false, platforms: {} });
|
|
}
|
|
|
|
if (!row) {
|
|
return resolve({ shouldUpload: false, platforms: {} });
|
|
}
|
|
|
|
const level = row.experience_level || 'beginner';
|
|
|
|
// Beginner/Intermediate는 기본 자동 업로드
|
|
if (level === 'beginner' || level === 'intermediate') {
|
|
return resolve({
|
|
shouldUpload: true,
|
|
platforms: {
|
|
youtube: true,
|
|
instagram: true,
|
|
tiktok: true
|
|
},
|
|
level
|
|
});
|
|
}
|
|
|
|
// Pro는 사용자 설정에 따름
|
|
if (level === 'pro') {
|
|
return resolve({
|
|
shouldUpload: row.auto_youtube || row.auto_instagram || row.auto_tiktok,
|
|
platforms: {
|
|
youtube: Boolean(row.auto_youtube),
|
|
instagram: Boolean(row.auto_instagram),
|
|
tiktok: Boolean(row.auto_tiktok)
|
|
},
|
|
level
|
|
});
|
|
}
|
|
|
|
resolve({ shouldUpload: false, platforms: {} });
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* SEO 데이터 자동 생성 (Gemini 사용)
|
|
*/
|
|
const generateAutoSeo = async (businessInfo, language = 'KO') => {
|
|
const { GoogleGenerativeAI } = require('@google/generative-ai');
|
|
const genAI = new GoogleGenerativeAI(process.env.VITE_GEMINI_API_KEY);
|
|
|
|
const langPrompts = {
|
|
KO: {
|
|
title: '한국어로',
|
|
tags: '한국어 해시태그',
|
|
},
|
|
EN: {
|
|
title: 'in English',
|
|
tags: 'English hashtags',
|
|
},
|
|
JA: {
|
|
title: '日本語で',
|
|
tags: '日本語ハッシュタグ',
|
|
},
|
|
ZH: {
|
|
title: '用中文',
|
|
tags: '中文标签',
|
|
},
|
|
TH: {
|
|
title: 'ในภาษาไทย',
|
|
tags: 'แฮชแท็กภาษาไทย',
|
|
},
|
|
VI: {
|
|
title: 'bằng tiếng Việt',
|
|
tags: 'hashtag tiếng Việt',
|
|
}
|
|
};
|
|
|
|
const langConfig = langPrompts[language] || langPrompts.KO;
|
|
|
|
const prompt = `
|
|
당신은 펜션/숙소 홍보 영상의 SEO 전문가입니다.
|
|
아래 정보를 바탕으로 YouTube, Instagram, TikTok용 SEO 데이터를 생성하세요.
|
|
|
|
펜션 정보:
|
|
- 이름: ${businessInfo.name || '펜션'}
|
|
- 설명: ${businessInfo.description || ''}
|
|
- 주소: ${businessInfo.address || ''}
|
|
- 카테고리: ${businessInfo.categories?.join(', ') || ''}
|
|
|
|
요구사항:
|
|
1. title: 매력적인 제목 (${langConfig.title}, 60자 이내)
|
|
2. description: 상세 설명 (${langConfig.title}, 200자 이내)
|
|
3. tags: 검색용 태그 10개 (${langConfig.tags}, 쉼표로 구분)
|
|
4. hashtags: 소셜미디어용 해시태그 15개 (${langConfig.tags}, #포함)
|
|
|
|
JSON 형식으로 응답하세요:
|
|
{
|
|
"title": "제목",
|
|
"description": "설명",
|
|
"tags": ["태그1", "태그2", ...],
|
|
"hashtags": "#태그1 #태그2 ..."
|
|
}
|
|
`;
|
|
|
|
try {
|
|
const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' });
|
|
const result = await model.generateContent(prompt);
|
|
const text = result.response.text();
|
|
|
|
// JSON 추출
|
|
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
if (jsonMatch) {
|
|
return JSON.parse(jsonMatch[0]);
|
|
}
|
|
|
|
// 기본값 반환
|
|
return {
|
|
title: `${businessInfo.name || '펜션'} - 힐링 여행`,
|
|
description: businessInfo.description || '아름다운 펜션에서 특별한 휴식을 경험하세요.',
|
|
tags: ['펜션', '여행', '힐링', '휴식', '숙소'],
|
|
hashtags: '#펜션 #여행 #힐링 #휴식 #숙소 #vacation #travel'
|
|
};
|
|
} catch (error) {
|
|
console.error('SEO 자동 생성 실패:', error);
|
|
return {
|
|
title: `${businessInfo.name || '펜션'} - 힐링 여행`,
|
|
description: businessInfo.description || '아름다운 펜션에서 특별한 휴식을 경험하세요.',
|
|
tags: ['펜션', '여행', '힐링', '휴식', '숙소'],
|
|
hashtags: '#펜션 #여행 #힐링 #휴식 #숙소 #vacation #travel'
|
|
};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 자동 업로드 실행
|
|
*/
|
|
const executeAutoUpload = async (userId, videoPath, businessInfo, language = 'KO') => {
|
|
const results = {
|
|
youtube: { success: false, error: null },
|
|
instagram: { success: false, error: null },
|
|
tiktok: { success: false, error: null }
|
|
};
|
|
|
|
try {
|
|
// 자동 업로드 설정 확인
|
|
const uploadConfig = await shouldAutoUpload(userId);
|
|
|
|
if (!uploadConfig.shouldUpload) {
|
|
console.log('자동 업로드 비활성화 - 사용자 설정에 따름');
|
|
return { skipped: true, reason: '자동 업로드 비활성화됨', results };
|
|
}
|
|
|
|
// SEO 데이터 자동 생성
|
|
const seoData = await generateAutoSeo(businessInfo, language);
|
|
console.log('SEO 데이터 생성 완료:', seoData.title);
|
|
|
|
// YouTube 업로드
|
|
if (uploadConfig.platforms.youtube) {
|
|
try {
|
|
const youtubeService = require('../youtubeService');
|
|
const youtubeResult = await youtubeService.uploadVideo(
|
|
userId,
|
|
videoPath,
|
|
seoData.title,
|
|
seoData.description + '\n\n' + seoData.hashtags,
|
|
seoData.tags
|
|
);
|
|
results.youtube = { success: true, videoId: youtubeResult.videoId };
|
|
console.log('YouTube 업로드 성공:', youtubeResult.videoId);
|
|
} catch (ytError) {
|
|
console.error('YouTube 업로드 실패:', ytError.message);
|
|
results.youtube = { success: false, error: ytError.message };
|
|
}
|
|
}
|
|
|
|
// Instagram 업로드
|
|
if (uploadConfig.platforms.instagram) {
|
|
try {
|
|
// Instagram은 비디오를 릴스로 업로드
|
|
const response = await fetch('http://localhost:5001/upload-reel', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
username: await getInstagramUsername(userId),
|
|
video_path: videoPath,
|
|
caption: seoData.description + '\n\n' + seoData.hashtags
|
|
})
|
|
});
|
|
|
|
if (response.ok) {
|
|
const result = await response.json();
|
|
results.instagram = { success: true, mediaId: result.media_id };
|
|
console.log('Instagram 업로드 성공');
|
|
} else {
|
|
const errorData = await response.json();
|
|
results.instagram = { success: false, error: errorData.detail || '업로드 실패' };
|
|
}
|
|
} catch (igError) {
|
|
console.error('Instagram 업로드 실패:', igError.message);
|
|
results.instagram = { success: false, error: igError.message };
|
|
}
|
|
}
|
|
|
|
// TikTok 업로드
|
|
if (uploadConfig.platforms.tiktok) {
|
|
try {
|
|
const tiktokService = require('../tiktokService');
|
|
const tiktokResult = await tiktokService.uploadVideo(
|
|
userId,
|
|
videoPath,
|
|
seoData.description + ' ' + seoData.hashtags
|
|
);
|
|
results.tiktok = { success: true, videoId: tiktokResult.videoId };
|
|
console.log('TikTok 업로드 성공');
|
|
} catch (ttError) {
|
|
console.error('TikTok 업로드 실패:', ttError.message);
|
|
results.tiktok = { success: false, error: ttError.message };
|
|
}
|
|
}
|
|
|
|
return {
|
|
skipped: false,
|
|
level: uploadConfig.level,
|
|
seoData,
|
|
results
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('자동 업로드 실행 중 오류:', error);
|
|
return {
|
|
skipped: false,
|
|
error: error.message,
|
|
results
|
|
};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 사용자의 Instagram 계정명 가져오기
|
|
*/
|
|
const getInstagramUsername = async (userId) => {
|
|
return new Promise((resolve, reject) => {
|
|
db.get(
|
|
'SELECT instagram_username FROM social_connections WHERE user_id = ?',
|
|
[userId],
|
|
(err, row) => {
|
|
if (err || !row) {
|
|
resolve(null);
|
|
} else {
|
|
resolve(row.instagram_username);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* 업로드 이력 저장
|
|
*/
|
|
const saveUploadHistory = async (userId, historyId, platform, result) => {
|
|
return new Promise((resolve, reject) => {
|
|
const query = `
|
|
INSERT INTO upload_history (user_id, history_id, platform, status, external_id, error_message, uploaded_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
|
|
`;
|
|
|
|
db.run(query, [
|
|
userId,
|
|
historyId,
|
|
platform,
|
|
result.success ? 'success' : 'failed',
|
|
result.videoId || result.mediaId || null,
|
|
result.error || null
|
|
], (err) => {
|
|
if (err) {
|
|
console.error('업로드 이력 저장 실패:', err);
|
|
}
|
|
resolve();
|
|
});
|
|
});
|
|
};
|
|
|
|
module.exports = {
|
|
shouldAutoUpload,
|
|
generateAutoSeo,
|
|
executeAutoUpload,
|
|
saveUploadHistory
|
|
};
|