false, 'message' => '유효하지 않은 ID']); exit; } /** * YouTube ID 추출 함수 */ function extract_yt_id($input) { if (preg_match('/^[a-zA-Z0-9_-]{11}$/', $input)) return $input; if (preg_match('#youtu\.be/([a-zA-Z0-9_-]{11})#', $input, $m)) return $m[1]; if (preg_match('#[?&]v=([a-zA-Z0-9_-]{11})#', $input, $m)) return $m[1]; return ''; } /** * Gemini AI 요약 함수 (보안 및 통신 강화) */ function get_gemini_summary($text) { if (empty(GEMINI_API_KEY) || GEMINI_API_KEY === 'YOUR_GEMINI_API_KEY_HERE') return null; // $url = 'https://generativelanguage.googleapis.com/v1/models/gemini-1.5-flash:generateContent?key=' . GEMINI_API_KEY; $url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=' . GEMINI_API_KEY; $data = [ 'contents' => [ [ 'parts' => [['text' => "너는 교육 콘텐츠 에디터야. 아래 유튜브 설명에서 타임라인(00:00)과 구독/좋아요 요청은 완전히 제외하고, 핵심 내용만 직장인을 위해 3문장 이내로 요약해줘:\n\n" . mb_substr($text, 0, 3000, 'UTF-8')]] ] ] ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_TIMEOUT, 15); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36'); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code === 200 && $response) { $res = json_decode($response, true); $result = $res['candidates'][0]['content']['parts'][0]['text'] ?? null; return $result ? trim($result) : null; } return null; } // 1. YouTube API 호출 (데이터 가져오기 로직 복구) $api_url = "https://www.googleapis.com/youtube/v3/videos?id={$video_id}&part=snippet,contentDetails&key=" . YOUTUBE_API_KEY; $yt_res = @file_get_contents($api_url); $yt_data = json_decode($yt_res, true); if (empty($yt_data['items'])) { echo json_encode(['success' => false, 'message' => '영상을 찾을 수 없습니다.']); exit; } $item = $yt_data['items'][0]; $yt_title = $item['snippet']['title'] ?? ''; $yt_desc_raw = $item['snippet']['description'] ?? ''; $duration = $item['contentDetails']['duration'] ?? 'PT0S'; // 2. 요약 처리 (AI 시도) $summary = get_gemini_summary($yt_desc_raw); // [핵심] AI가 실패하거나, 결과에 여전히 타임라인(00:00)이 포함된 경우 강력한 수동 필터 작동 if (!$summary || preg_match('/[0-9]{1,2}:[0-9]{2}/', $summary)) { $text = $yt_desc_raw; // (1) 타임라인 제거 (줄 시작이 숫자:숫자인 모든 줄 삭제) $text = preg_replace('/^[ ]*[0-9]{1,2}:[0-9]{2}.*$/m', '', $text); // (2) 구독/좋아요/광고 문구가 포함된 문장 삭제 $patterns = ['구독', '좋아요', '알림설정', '인스타그램', '페이스북', '문의']; foreach ($patterns as $p) { $text = preg_replace('/[^.!?\n]*?' . $p . '[^.!?\n]*?[.!?\n]?/u', '', $text); } // (3) 해시태그 및 이모지 제거 $text = preg_replace('/#[^\s#]+/u', '', $text); $text = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $text); // (4) 공백 정리 및 200자 절삭 $text = preg_replace('/\s+/', ' ', trim($text)); $summary = mb_substr($text, 0, 200, 'UTF-8'); if (mb_strlen($text, 'UTF-8') > 200) $summary .= '...'; } // 3. 재생 시간 계산 (ISO 8601 -> 초) preg_match('/PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/', $duration, $m); $content_tm = ((int) ($m[1] ?? 0) * 3600) + ((int) ($m[2] ?? 0) * 60) + (int) ($m[3] ?? 0); // 최종 결과 출력 echo json_encode([ 'success' => true, 'yt_title' => $yt_title, 'description' => $summary, 'content_tm' => $content_tm, 'duration_fmt' => sprintf('%d:%02d', intdiv($content_tm, 60), $content_tm % 60), ], JSON_UNESCAPED_UNICODE); exit;