diff --git a/examples/youtube_analyzer_multimodal.ipynb b/examples/youtube_analyzer_multimodal.ipynb new file mode 100644 index 000000000..d859d3cc9 --- /dev/null +++ b/examples/youtube_analyzer_multimodal.ipynb @@ -0,0 +1,420 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/anaconda3/envs/kospi/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "# Import required libraries for JSON parsing, data modeling and LLM integration\n", + "from langchain_core.output_parsers import JsonOutputParser\n", + "from pydantic import BaseModel, Field\n", + "from langchain_google_genai import ChatGoogleGenerativeAI\n", + "from typing import List\n", + "\n", + "\n", + "# Define VideoAnalysis class to structure video metadata\n", + "class VideoAnalysis(BaseModel):\n", + " ad_type: str = Field(description=\"Type of advertisement (e.g., Brand ad, Product ad, etc.)\")\n", + " main_category: str = Field(description=\"Main category of the video\")\n", + " sub_categories: List[str] = Field(description=\"List of video sub-categories\")\n", + " main_theme_message: str = Field(description=\"Main theme and message of the video\")\n", + " tone_and_manner: str = Field(description=\"Tone and manner of the video\")\n", + " target_audience: List[str] = Field(description=\"Target customer segments\")\n", + " key_keywords: List[str] = Field(description=\"List of key keywords\")\n", + " suitable_industries: List[str] = Field(description=\"List of suitable industries\")\n", + " expected_viewer_interest: str = Field(description=\"Expected viewer interest level (High/Medium/Low)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_google_genai import ChatGoogleGenerativeAI\n", + "\n", + "gemini_llm = ChatGoogleGenerativeAI(\n", + " model=\"gemini-1.5-pro\",\n", + " temperature=0\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import google.generativeai as genai\n", + "import time\n", + "import os\n", + "import json\n", + "import yt_dlp\n", + "from dotenv import load_dotenv\n", + "load_dotenv()\n", + "\n", + "class GeminiVideoAnalyzer:\n", + " def __init__(self):\n", + " \n", + " self.GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')\n", + " genai.configure(api_key=self.GOOGLE_API_KEY)\n", + " self.model = genai.GenerativeModel(model_name=\"gemini-1.5-pro\")\n", + " \n", + " def download_video(self, url: str) -> str:\n", + " \"\"\"Download YouTube video\"\"\"\n", + " video_dir = \"download_video\"\n", + " os.makedirs(video_dir, exist_ok=True)\n", + " \n", + " try:\n", + " ydl_opts = {\n", + " 'format': 'bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/best[height<=720][ext=mp4]/best',\n", + " 'outtmpl': os.path.join(video_dir, '%(title)s.%(ext)s'),\n", + " 'quiet': True,\n", + " 'no_warnings': True,\n", + " 'extract_flat': False,\n", + " 'merge_output_format': 'mp4'\n", + " }\n", + " \n", + " with yt_dlp.YoutubeDL(ydl_opts) as ydl:\n", + " info = ydl.extract_info(url, download=True)\n", + " video_path = ydl.prepare_filename(info)\n", + " \n", + " if not os.path.exists(video_path):\n", + " raise FileNotFoundError(f\"Downloaded video not found: {video_path}\")\n", + " \n", + " print(f\"Video download completed: {video_path}\")\n", + " return video_path\n", + " \n", + " except Exception as e:\n", + " print(f\"Error occurred during video download: {str(e)}\")\n", + " raise\n", + "\n", + " def analyze_video(self, video_path: str) -> dict:\n", + " \"\"\"Analyze video using Gemini\"\"\"\n", + " try:\n", + " video_file = genai.upload_file(video_path, mime_type='video/mp4')\n", + " print(f\"Starting file upload: {video_path}\")\n", + "\n", + " if video_file.state.name == \"PROCESSING\":\n", + " print('Processing upload...', end='', flush=True)\n", + " time.sleep(5)\n", + " video_file = genai.get_file(video_file.name)\n", + "\n", + " if video_file.state.name == \"FAILED\":\n", + " raise ValueError(f\"File processing failed: {video_file.state}\")\n", + " elif video_file.state.name == \"ACTIVE\":\n", + " print(f\"\\nFile processing complete! Status: {video_file.state.name}\")\n", + "\n", + " prompt = \"\"\"\n", + " Please analyze the main content of this video. Include the following items:\n", + " \n", + " 1. Ad type (Product ad, Brand ad, etc.)\n", + " 2. Main category\n", + " 3. Sub categories\n", + " 4. Main theme/message\n", + " 5. Tone and manner\n", + " 6. Target audience\n", + " 7. Key keywords\n", + " 8. Suitable industries\n", + " 9. Expected viewer interest (High/Medium/Low)\n", + " \n", + " Please respond in JSON format.\n", + " \"\"\"\n", + "\n", + " print(\"Requesting Gemini analysis...\")\n", + " response = self.model.generate_content(\n", + " [video_file, prompt],\n", + " request_options={\"timeout\": 600}\n", + " )\n", + " \n", + " return response.text\n", + "\n", + " except Exception as e:\n", + " print(f\"Error occurred during video analysis: {str(e)}\")\n", + " raise\n", + " finally:\n", + " # Clean up temporary files\n", + " if os.path.exists(video_path):\n", + " try:\n", + " os.remove(video_path)\n", + " except Exception as e:\n", + " print(f\"Failed to delete temporary file: {str(e)}\")\n", + "\n", + " def analyze_youtube_video(self, url: str) -> dict:\n", + " \"\"\"Run complete analysis process from YouTube URL\"\"\"\n", + " try:\n", + " video_path = self.download_video(url)\n", + " result = self.analyze_video(video_path)\n", + " return result\n", + " except Exception as e:\n", + " print(f\"Analysis failed: {str(e)}\")\n", + " raise" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Video download completed: download_video/Google Gemini | 엉뚱발랄 채령과 Gemini가 새로 개발(?)한 젠가 떡볶이🤣 | 제미나이 키친 - 채령 15s ver..mp4\n", + "Starting file upload: download_video/Google Gemini | 엉뚱발랄 채령과 Gemini가 새로 개발(?)한 젠가 떡볶이🤣 | 제미나이 키친 - 채령 15s ver..mp4\n", + "Processing upload...\n", + "File processing complete! Status: ACTIVE\n", + "Requesting Gemini analysis...\n", + "\"Sure, here’s the video analysis in JSON format.\\n\\n```json\\n{\\n \\\"ad_type\\\": \\\"Product Ad\\\",\\n \\\"main_category\\\": \\\"Technology\\\",\\n \\\"sub_categories\\\": [\\\"Software\\\", \\\"Artificial Intelligence\\\", \\\"Mobile Apps\\\"],\\n \\\"main_theme_message\\\": \\\"Introducing Google Gemini, a helpful AI assistant that can answer questions, provide information, and even offer creative inspiration for cooking.\\\",\\n \\\"tone_and_manner\\\": \\\"Friendly, Playful, Informative\\\",\\n \\\"target_audience\\\": [\\\"Tech-savvy individuals\\\", \\\"Early adopters\\\", \\\"People interested in AI\\\", \\\"Users seeking convenient information access\\\", \\\"Content creators\\\"],\\n \\\"key_keywords\\\": [\\\"Google Gemini\\\", \\\"AI assistant\\\", \\\"Artificial intelligence\\\", \\\"Mobile app\\\", \\\"Information retrieval\\\", \\\"Creative assistance\\\", \\\"Cooking inspiration\\\", \\\"Tteokbokki\\\"],\\n \\\"suitable_industries\\\": [\\\"Technology\\\", \\\"Food and beverage\\\", \\\"Content creation\\\", \\\"Education\\\", \\\"Customer service\\\"],\\n \\\"expected_viewer_interest\\\": \\\"Medium\\\"\\n}\\n\\n```\"\n" + ] + } + ], + "source": [ + "url = \"https://www.youtube.com/watch?v=XMgt2e7l6hk\"\n", + "# 분석기 초기화 및 실행\n", + "try:\n", + " analyzer = GeminiVideoAnalyzer()\n", + " result = analyzer.analyze_youtube_video(url)\n", + " print(json.dumps(result, indent=2, ensure_ascii=False))\n", + "except Exception as e:\n", + " print(f\"error in run: {str(e)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.output_parsers import JsonOutputParser\n", + "from langchain_core.prompts import PromptTemplate\n", + "\n", + "def create_video_analysis_chain():\n", + " \"\"\"Function to create LangChain chain for video analysis\"\"\"\n", + " parser = JsonOutputParser(pydantic_object=VideoAnalysis)\n", + " \n", + " prompt = PromptTemplate(\n", + " template=\"\"\"Please analyze the given video and provide detailed information.\n", + "\n", + " {format_instructions}\n", + "\n", + " Video description: {input}\n", + " \"\"\",\n", + " input_variables=[\"input\"],\n", + " partial_variables={\"format_instructions\": parser.get_format_instructions()},\n", + " )\n", + " \n", + " chain = prompt | gemini_llm | parser\n", + " return chain\n", + "\n", + "def analyze_video(video_description: str, context: str = \"\"):\n", + " \"\"\"\n", + " Function that analyzes video and returns structured results\n", + " \n", + " Args:\n", + " video_description (str): Description of video to analyze\n", + " context (str): Additional context information (optional)\n", + " \n", + " Returns:\n", + " VideoAnalysis: Analysis results\n", + " \"\"\"\n", + " chain = create_video_analysis_chain()\n", + " result = chain.invoke({\n", + " \"input\": video_description,\n", + " })\n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'ad_type': 'Product Ad', 'main_category': 'Technology', 'sub_categories': ['Software', 'Artificial Intelligence', 'Mobile Apps'], 'main_theme_message': 'Introducing Google Gemini, a helpful AI assistant that can answer questions, provide information, and even offer creative inspiration for cooking.', 'tone_and_manner': 'Friendly, Playful, Informative', 'target_audience': ['Tech-savvy individuals', 'Early adopters', 'People interested in AI', 'Users seeking convenient information access', 'Content creators'], 'key_keywords': ['Google Gemini', 'AI assistant', 'Artificial intelligence', 'Mobile app', 'Information retrieval', 'Creative assistance', 'Cooking inspiration', 'Tteokbokki'], 'suitable_industries': ['Technology', 'Food and beverage', 'Content creation', 'Education', 'Customer service'], 'expected_viewer_interest': 'Medium'}\n" + ] + } + ], + "source": [ + "final_result = analyze_video(result)\n", + "print(final_result)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "벡터 스토어 초기화 시작...\n", + "CSV 파일 로드 완료: 6 개의 광고 데이터\n", + "Document 객체 생성 완료: 6 개\n", + "벡터 스토어 생성 완료\n", + "벡터 스토어 저장 완료: ad_vectorstore\n", + "\n", + "Similar Ad Search Results:\n", + "Filename: [ KT | Microsoft 파트너십편 ] KT가 만듭니다. 글로벌 K-AI.mp4\n", + "Category: IT/기술\n", + "Ad Type: 브랜드 광고\n", + "Target Audience: ['기업 관계자', 'IT 업계']\n", + "\n", + "Ad Content:\n", + "광고 유형: 브랜드 광고\n", + " 주요 카테고리: IT/기술\n", + " 서브 카테고리: 인공지능(AI), 통신, 소프트웨어\n", + " 주요 테마: KT-Microsoft AI 파트너십\n", + " 톤앤매너: 밝고 긍정적, 미래지향적\n", + " 타겟 고객: 기업 관계자, IT 업계\n", + " 키워드: 인공지능, AI, KT, Microsoft\n", + " 적합 산업군: IT/기술, 통신, 소프트웨어, 교육\n" + ] + } + ], + "source": [ + "from app.vector import AdVectorDB\n", + "from app.vector_search import AdVectorSearch\n", + "# Initialize and save vector DB\n", + "csv_path = \"/Users/kdb/Desktop/youtube_add/final_ad_analysis.csv\"\n", + "vector_db = AdVectorDB(csv_path)\n", + "vector_db.initialize_vector_store(\"ad_vectorstore\")\n", + "\n", + "# Execute vector search\n", + "from app.vector_search import AdVectorSearch\n", + "vector_search = AdVectorSearch(\"ad_vectorstore\")\n", + "similar_video = vector_search.search_similar_ads(result)\n", + "\n", + "# Print results\n", + "print(\"\\nSimilar Ad Search Results:\")\n", + "print(f\"Filename: {similar_video['metadata']['filename']}\")\n", + "print(f\"Category: {similar_video['metadata']['main_category']}\")\n", + "print(f\"Ad Type: {similar_video['metadata']['content_type']}\")\n", + "print(f\"Target Audience: {similar_video['metadata']['target_audience']}\")\n", + "print(\"\\nAd Content:\")\n", + "print(similar_video['content'])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/Users/kdb/Desktop/youtube_add/KT_ad/[ KT | Microsoft 파트너십편 ] KT가 만듭니다. 글로벌 K-AI.mp4'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "similar_video['metadata']['video_path']" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Playing file: /Users/kdb/Desktop/youtube_add/KT_ad/[ 팬메이드K-AI 아이디어 챌린지 ] No.01 ‘배려심 넘치는 조명’편 | KT.mp4\n" + ] + } + ], + "source": [ + "from IPython.display import Video, display\n", + "import os\n", + "from pathlib import Path\n", + "import urllib.parse\n", + "\n", + "def play_video(similar_video):\n", + " \"\"\"Play video\"\"\"\n", + " try:\n", + " base_dir = Path(\"/Users/kdb/Desktop/youtube_add/KT_ad\")\n", + " filename = \"[ 팬메이드K-AI 아이디어 챌린지 ] No.01 ‘배려심 넘치는 조명’편 | KT.mp4\"\n", + " \n", + " # Find actual file in current directory\n", + " for file in base_dir.iterdir():\n", + " if file.name == filename:\n", + " video_path = file\n", + " break\n", + " \n", + " if 'video_path' in locals():\n", + " # Display video (with size adjustment)\n", + " display(Video(str(video_path), embed=True, width=800, height=450))\n", + " print(f\"Playing file: {video_path}\")\n", + " else:\n", + " print(\"File not found.\")\n", + " print(\"\\nDirectory contents:\")\n", + " for file in base_dir.iterdir():\n", + " print(file.name)\n", + " \n", + " except Exception as e:\n", + " print(f\"Error occurred while playing video: {e}\")\n", + " if 'video_path' in locals():\n", + " print(f\"Attempted path: {video_path}\")\n", + "\n", + "# Play video\n", + "play_video(similar_video)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kospi", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}