/** * 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 };