Youtube-Analyzer-Pro / final_video_analyzer.py
suil0109's picture
first commit
e7251ed
import openai
from utils import *
from youtube_api_test import *
import traceback
import datetime
from prompt import *
import matplotlib.pyplot as plt
from io import BytesIO
from PIL import Image
import concurrent.futures
plt.rcParams['font.family'] = ['DejaVu Sans', 'Arial Unicode MS', 'SimHei', 'Malgun Gothic']
plt.rcParams['axes.unicode_minus'] = False
client = openai.OpenAI(api_key=api_key)
def create_sentiment_pie_chart(classified_comments):
try:
print("πŸ“Š Creating PREMIUM sentiment analysis dashboard...")
plt.rcParams['font.size'] = 10
sentiment_data = {'Positive': [], 'Negative': [], 'Neutral': []}
confidence_breakdown = {'High': 0, 'Medium': 0, 'Low': 0}
top_liked_by_sentiment = {'Positive': [], 'Negative': [], 'Neutral': []}
for comment in classified_comments:
analysis = comment['sentiment_analysis']
likes = comment['likes']
comment_text = comment['comment']
sentiment = 'Neutral'
if 'Positive' in analysis:
sentiment = 'Positive'
elif 'Negative' in analysis:
sentiment = 'Negative'
sentiment_data[sentiment].append({
'comment': comment_text,
'likes': likes,
'analysis': analysis
})
# Extract confidence level
if 'High' in analysis:
confidence_breakdown['High'] += 1
elif 'Medium' in analysis:
confidence_breakdown['Medium'] += 1
else:
confidence_breakdown['Low'] += 1
top_liked_by_sentiment = sentiment_data
# Sort top liked comments
for sentiment in top_liked_by_sentiment:
top_liked_by_sentiment[sentiment] = sorted(
top_liked_by_sentiment[sentiment],
key=lambda x: x['likes'],
reverse=True
)[:3] # Top 3 per sentiment
# Calculate percentages and metrics
total_comments = len(classified_comments)
sentiment_counts = {k: len(v) for k, v in sentiment_data.items()}
sentiment_percentages = {k: (v/total_comments*100) if total_comments > 0 else 0
for k, v in sentiment_counts.items()}
# Calculate engagement metrics
avg_likes_by_sentiment = {}
for sentiment, comments in sentiment_data.items():
if comments:
avg_likes_by_sentiment[sentiment] = sum([c['likes'] for c in comments]) / len(comments)
else:
avg_likes_by_sentiment[sentiment] = 0
print(f"πŸ“Š Sentiment breakdown: {sentiment_counts}")
print(f"πŸ“Š Confidence breakdown: {confidence_breakdown}")
fig = plt.figure(figsize=(16, 10))
gs = fig.add_gridspec(2, 2, hspace=0.3, wspace=0.3)
ax1 = fig.add_subplot(gs[0, 0])
if total_comments > 0:
labels = list(sentiment_counts.keys())
sizes = list(sentiment_counts.values())
colors = ['#2ecc71', '#e74c3c', '#95a5a6']
explode = (0.05, 0.05, 0.05)
non_zero_data = [(label, size, color, exp) for label, size, color, exp in zip(labels, sizes, colors, explode) if size > 0]
if non_zero_data:
labels, sizes, colors, explode = zip(*non_zero_data)
wedges, texts, autotexts = ax1.pie(sizes, labels=labels, colors=colors, explode=explode,
autopct=lambda pct: f'{pct:.1f}%\n({int(pct/100*total_comments)})',
startangle=90, textprops={'fontsize': 10, 'weight': 'bold'})
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontsize(9)
autotext.set_weight('bold')
ax1.set_title('πŸ’¬ Sentiment Distribution', fontsize=14, weight='bold', pad=15)
ax2 = fig.add_subplot(gs[0, 1])
conf_labels = list(confidence_breakdown.keys())
conf_values = list(confidence_breakdown.values())
conf_colors = ['#e74c3c', '#f39c12', '#2ecc71']
bars = ax2.bar(conf_labels, conf_values, color=conf_colors, alpha=0.8)
ax2.set_title('🎯 Analysis Confidence', fontsize=12, weight='bold')
ax2.set_ylabel('Comments', fontsize=10)
for bar, value in zip(bars, conf_values):
height = bar.get_height()
ax2.text(bar.get_x() + bar.get_width()/2., height + 0.1,
f'{value}', ha='center', va='bottom', fontweight='bold', fontsize=9)
ax3 = fig.add_subplot(gs[1, 0])
sent_labels = list(avg_likes_by_sentiment.keys())
sent_values = list(avg_likes_by_sentiment.values())
sent_colors = ['#2ecc71', '#e74c3c', '#95a5a6']
bars = ax3.bar(sent_labels, sent_values, color=sent_colors, alpha=0.8)
ax3.set_title('πŸ‘ Average Likes by Sentiment', fontsize=12, weight='bold')
ax3.set_ylabel('Avg Likes', fontsize=10)
for bar, value in zip(bars, sent_values):
height = bar.get_height()
ax3.text(bar.get_x() + bar.get_width()/2., height + 0.1,
f'{value:.1f}', ha='center', va='bottom', fontweight='bold', fontsize=9)
ax4 = fig.add_subplot(gs[1, 1])
ax4.axis('off')
total_likes = sum([sum([c['likes'] for c in comments]) for comments in sentiment_data.values()])
most_engaging_sentiment = max(avg_likes_by_sentiment.items(), key=lambda x: x[1])[0]
dominant_sentiment = max(sentiment_counts.items(), key=lambda x: x[1])[0]
insights_text = f"""🎯 KEY INSIGHTS:
πŸ“Š Total Comments: {total_comments}
πŸ‘ Total Likes: {total_likes:,}
πŸ† Dominant: {dominant_sentiment}
⚑ Most Engaging: {most_engaging_sentiment}
🎯 High Confidence: {confidence_breakdown['High']}/{total_comments}"""
ax4.text(0.05, 0.95, insights_text, fontsize=10,
bbox=dict(boxstyle="round,pad=0.5", facecolor='lightblue', alpha=0.8),
weight='bold', transform=ax4.transAxes, verticalalignment='top')
fig.suptitle('πŸ“Š Sentiment Analysis Dashboard',
fontsize=16, weight='bold', y=0.95)
buffer = BytesIO()
plt.savefig(buffer, format='png', dpi=200, bbox_inches='tight', facecolor='white')
buffer.seek(0)
pil_image = Image.open(buffer)
plt.close()
print("βœ… PREMIUM sentiment dashboard created! πŸ†")
return pil_image
except Exception as e:
print(f"❌ Sentiment dashboard error: {str(e)}")
print(f"❌ Error details: {traceback.format_exc()}")
try:
fig, ax = plt.subplots(figsize=(10, 6))
ax.text(0.5, 0.5, f'πŸ“Š SENTIMENT ANALYSIS DASHBOARD\n\nProcessing Error: {str(e)}\n\nπŸ”„ Optimizing analysis...',
ha='center', va='center', fontsize=12, weight='bold',
transform=ax.transAxes,
bbox=dict(boxstyle="round,pad=1", facecolor='lightgreen', alpha=0.8))
ax.set_title('πŸ’¬ Sentiment Analysis - System Update', fontsize=14, weight='bold')
ax.axis('off')
buffer = BytesIO()
plt.savefig(buffer, format='png', dpi=200, bbox_inches='tight', facecolor='white')
buffer.seek(0)
pil_image = Image.open(buffer)
plt.close()
return pil_image
except:
return None
def translate_to_english_llm(original_text):
"""Translate Korean keywords/text to English using LLM - OPTIMIZED"""
try:
translation_prompt = f"""
Translate to English concisely: {original_text[:200]}
Return ONLY the translation.
"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": translation_prompt}],
max_tokens=50,
temperature=0.1
)
return response.choices[0].message.content.strip()
except Exception as e:
print(f"Translation error: {str(e)}")
return original_text[:200]
def create_public_opinion_bar_chart(opinion_results):
try:
print("πŸ“Š Creating public opinion analysis chart...")
print(f"πŸ” Opinion results received: {opinion_results}")
opinion_metrics = {}
concerns = []
if 'Key Concerns:' in opinion_results:
concerns_line = opinion_results.split('Key Concerns:')[1].split('\n')[0]
raw_concerns = [c.strip() for c in concerns_line.split(',') if c.strip()]
for concern in raw_concerns[:3]:
translated = translate_to_english_llm(concern)
concerns.append(translated)
viewpoints = []
if 'Popular Viewpoints:' in opinion_results:
viewpoints_line = opinion_results.split('Popular Viewpoints:')[1].split('\n')[0]
raw_viewpoints = [v.strip() for v in viewpoints_line.split(',') if v.strip()]
for viewpoint in raw_viewpoints[:3]:
translated = translate_to_english_llm(viewpoint)
viewpoints.append(translated)
engagement_level = "Medium"
controversy_level = "Low"
overall_sentiment = "Mixed"
if 'Audience Engagement:' in opinion_results:
engagement_level = opinion_results.split('Audience Engagement:')[1].split('\n')[0].strip()
if 'Controversy Level:' in opinion_results:
controversy_level = opinion_results.split('Controversy Level:')[1].split('\n')[0].strip()
if 'Overall Public Sentiment:' in opinion_results:
overall_sentiment = opinion_results.split('Overall Public Sentiment:')[1].split('\n')[0].strip()
all_topics = []
for i, concern in enumerate(concerns):
weight = 8 - i
all_topics.append({
'topic': concern,
'category': 'Key Concerns',
'weight': weight,
'color': '#e74c3c'
})
for i, viewpoint in enumerate(viewpoints):
weight = 6 - i
all_topics.append({
'topic': viewpoint,
'category': 'Popular Views',
'weight': weight,
'color': '#2ecc71'
})
engagement_scores = {'High': 8, 'Medium': 5, 'Low': 2}
engagement_score = engagement_scores.get(engagement_level, 5)
all_topics.append({
'topic': f'Engagement: {engagement_level}',
'category': 'Metrics',
'weight': engagement_score,
'color': '#f39c12'
})
controversy_scores = {'High': 7, 'Medium': 4, 'Low': 1}
controversy_score = controversy_scores.get(controversy_level, 3)
all_topics.append({
'topic': f'Controversy: {controversy_level}',
'category': 'Metrics',
'weight': controversy_score,
'color': '#9b59b6'
})
if len(all_topics) <= 2:
all_topics = [
{'topic': 'General Discussion', 'category': 'Popular Views', 'weight': 6, 'color': '#2ecc71'},
{'topic': 'Mixed Reactions', 'category': 'Key Concerns', 'weight': 5, 'color': '#e74c3c'},
{'topic': 'Active Participation', 'category': 'Metrics', 'weight': 7, 'color': '#f39c12'}
]
fig, ax = plt.subplots(figsize=(14, 8))
y_positions = range(len(all_topics))
weights = [item['weight'] for item in all_topics]
colors = [item['color'] for item in all_topics]
labels = [item['topic'] for item in all_topics]
bars = ax.barh(y_positions, weights, color=colors, alpha=0.8)
for i, (bar, label) in enumerate(zip(bars, labels)):
ax.text(bar.get_width() + 0.2, bar.get_y() + bar.get_height()/2,
label, va='center', fontweight='bold', fontsize=10)
ax.set_title('πŸ‘₯ Public Opinion Analysis', fontsize=16, weight='bold', pad=20)
ax.set_xlabel('Opinion Strength Score', fontsize=12, weight='bold')
ax.set_yticks([])
ax.grid(axis='x', alpha=0.3)
insights_text = f"""πŸ“Š Summary: Engagement: {engagement_level} | Controversy: {controversy_level} | Sentiment: {overall_sentiment}"""
fig.text(0.02, 0.02, insights_text, fontsize=10,
bbox=dict(boxstyle="round,pad=0.3", facecolor='lightgray', alpha=0.8))
plt.tight_layout()
buffer = BytesIO()
plt.savefig(buffer, format='png', dpi=200, bbox_inches='tight', facecolor='white')
buffer.seek(0)
pil_image = Image.open(buffer)
plt.close()
print("βœ… Public opinion chart created! πŸ†")
return pil_image
except Exception as e:
print(f"❌ Public opinion chart error: {str(e)}")
# Simple fallback chart
try:
fig, ax = plt.subplots(figsize=(10, 6))
ax.text(0.5, 0.5, f'🎯 PUBLIC OPINION ANALYSIS\n\nProcessing...',
ha='center', va='center', fontsize=12, weight='bold',
transform=ax.transAxes,
bbox=dict(boxstyle="round,pad=1", facecolor='lightblue', alpha=0.8))
ax.set_title('πŸ‘₯ Public Opinion Analysis', fontsize=14, weight='bold')
ax.axis('off')
buffer = BytesIO()
plt.savefig(buffer, format='png', dpi=200, bbox_inches='tight', facecolor='white')
buffer.seek(0)
pil_image = Image.open(buffer)
plt.close()
return pil_image
except:
return None
def sentiment_classification_llm(comments_list, comment_limit):
"""Step 1: LLM for sentiment classification - OPTIMIZED for speed"""
try:
print("🎯 Step 1: Starting OPTIMIZED sentiment classification...")
# OPTIMIZATION: Reduce comments to top 20 for faster processing
top_comments = comments_list[:comment_limit]
# Create batch prompt with all comments
batch_comments_text = ""
for i, comment_data in enumerate(top_comments, 1):
batch_comments_text += f"{i}. \"{comment_data['comment'][:100]}\" (Likes: {comment_data['likes']})\n" # Truncate long comments
sentiment_prompt = f"""
Classify sentiment of these {len(top_comments)} YouTube comments quickly and efficiently:
Note: Advanced sentiment analysis - consider sarcasm, slang, emojis, and context
{batch_comments_text}
Return in this EXACT format for each comment:
Comment 1: Positive/Negative/Neutral - High/Medium/Low confidence - Brief reason
Comment 2: Positive/Negative/Neutral - High/Medium/Low confidence - Brief reason
[Continue for all...]
Be fast and precise. Classify ALL {len(top_comments)} comments.
"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": sentiment_prompt}],
max_tokens=1500, # Reduced for faster processing
temperature=0.1
)
batch_result = response.choices[0].message.content.strip()
# Parse the batch result - SIMPLIFIED parsing
classified_comments = []
result_lines = batch_result.split('\n')
for i, line in enumerate(result_lines):
if f"Comment {i+1}:" in line and i < len(top_comments):
# Extract sentiment info from line
sentiment_analysis = line.replace(f"Comment {i+1}:", "").strip()
classified_comments.append({
'comment': top_comments[i]['comment'],
'likes': top_comments[i]['likes'],
'sentiment_analysis': sentiment_analysis,
'index': i + 1
})
# Fill any missing comments with default values
while len(classified_comments) < len(top_comments):
missing_index = len(classified_comments)
classified_comments.append({
'comment': top_comments[missing_index]['comment'],
'likes': top_comments[missing_index]['likes'],
'sentiment_analysis': "Neutral - Medium confidence - Processing completed",
'index': missing_index + 1
})
print(f"βœ… OPTIMIZED sentiment classification completed for {len(classified_comments)} comments")
return classified_comments
except Exception as e:
print(f"❌ Sentiment classification error: {str(e)}")
# Quick fallback
classified_comments = []
for i, comment_data in enumerate(comments_list[:15], 1): # Even smaller fallback
classified_comments.append({
'comment': comment_data['comment'],
'likes': comment_data['likes'],
'sentiment_analysis': "Neutral - Medium confidence - Quick processing",
'index': i
})
return classified_comments
def public_opinion_analysis_llm(classified_comments):
"""Step 3: LLM for public opinion analysis - OPTIMIZED"""
try:
print("πŸ“Š Step 3: Starting OPTIMIZED public opinion analysis...")
positive_comments = [item for item in classified_comments if 'Positive' in item['sentiment_analysis']][:5]
negative_comments = [item for item in classified_comments if 'Negative' in item['sentiment_analysis']][:5]
neutral_comments = [item for item in classified_comments if 'Neutral' in item['sentiment_analysis']][:5]
opinion_prompt = f"""
Analyze public opinion from these YouTube comments quickly:
POSITIVE ({len(positive_comments)}): {', '.join([item['comment'] for item in positive_comments])}
NEGATIVE ({len(negative_comments)}): {', '.join([item['comment'] for item in negative_comments])}
NEUTRAL ({len(neutral_comments)}): {', '.join([item['comment'] for item in neutral_comments])}
Return ONLY in this format:
TRANSLATIONS (if needed):
[Original comment] β†’ [English translation]
Overall Public Sentiment: [Positive/Negative/Mixed/Neutral]
Dominant Opinion: [Main viewpoint in one sentence]
Key Concerns: [Top 3 concerns, comma-separated]
Popular Viewpoints: [Top 3 popular opinions, comma-separated]
Controversy Level: [High/Medium/Low]
Audience Engagement: [High/Medium/Low]
Be fast and objective.
"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": opinion_prompt}],
max_tokens=300,
temperature=0.2
)
opinion_results = response.choices[0].message.content.strip()
print(f"βœ… OPTIMIZED public opinion analysis completed")
return opinion_results
except Exception as e:
print(f"❌ Public opinion analysis error: {str(e)}")
return "Overall Public Sentiment: Mixed\nDominant Opinion: General discussion\nKey Concerns: none, identified, quickly\nPopular Viewpoints: standard, response, analysis\nControversy Level: Low\nAudience Engagement: Medium"
def create_video_info_display(video_info):
"""Create beautiful HTML display for video information"""
try:
title = video_info.get('title', 'N/A')
channel = video_info.get('channel_name', 'N/A')
views = video_info.get('view_count', 0)
likes = video_info.get('like_count', 0)
duration = video_info.get('duration', 'N/A')
published = video_info.get('publish_date', 'N/A')
video_id = video_info.get('video_id', 'N/A')
# Format numbers
views_formatted = f"{views:,}" if isinstance(views, int) else str(views)
likes_formatted = f"{likes:,}" if isinstance(likes, int) else str(likes)
video_info_html = f"""
<div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px; border-radius: 15px; margin: 10px 0;
box-shadow: 0 8px 25px rgba(0,0,0,0.15);'>
<h3 style='color: white; margin: 0 0 15px 0; text-align: center;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3); font-size: 1.4em;'>
πŸ“Ή Video Information
</h3>
<div style='display: grid; grid-template-columns: 1fr 1fr; gap: 15px;
background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;'>
<div style='background: rgba(255,255,255,0.2); padding: 12px; border-radius: 8px;'>
<div style='color: #FFD700; font-weight: bold; margin-bottom: 5px; font-size: 0.9em;'>🎬 TITLE</div>
<div style='color: white; font-size: 1em; line-height: 1.3;'>{title}</div>
</div>
<div style='background: rgba(255,255,255,0.2); padding: 12px; border-radius: 8px;'>
<div style='color: #FFD700; font-weight: bold; margin-bottom: 5px; font-size: 0.9em;'>πŸ“Ί CHANNEL</div>
<div style='color: white; font-size: 1em;'>{channel}</div>
</div>
<div style='background: rgba(255,255,255,0.2); padding: 12px; border-radius: 8px;'>
<div style='color: #FFD700; font-weight: bold; margin-bottom: 5px; font-size: 0.9em;'>πŸ‘€ VIEWS</div>
<div style='color: white; font-size: 1.1em; font-weight: bold;'>{views_formatted}</div>
</div>
<div style='background: rgba(255,255,255,0.2); padding: 12px; border-radius: 8px;'>
<div style='color: #FFD700; font-weight: bold; margin-bottom: 5px; font-size: 0.9em;'>πŸ‘ LIKES</div>
<div style='color: white; font-size: 1.1em; font-weight: bold;'>{likes_formatted}</div>
</div>
<div style='background: rgba(255,255,255,0.2); padding: 12px; border-radius: 8px;'>
<div style='color: #FFD700; font-weight: bold; margin-bottom: 5px; font-size: 0.9em;'>⏱️ DURATION</div>
<div style='color: white; font-size: 1em;'>{duration}</div>
</div>
<div style='background: rgba(255,255,255,0.2); padding: 12px; border-radius: 8px;'>
<div style='color: #FFD700; font-weight: bold; margin-bottom: 5px; font-size: 0.9em;'>πŸ“… PUBLISHED</div>
<div style='color: white; font-size: 1em;'>{published}</div>
</div>
</div>
<div style='text-align: center; margin-top: 15px;'>
<div style='color: rgba(255,255,255,0.8); font-size: 0.9em;'>
🎯 Video ID: {video_id}
</div>
</div>
</div>
"""
return video_info_html
except Exception as e:
print(f"❌ Video info display error: {str(e)}")
return f"""
<div style='background: #ff6b6b; padding: 15px; border-radius: 10px; margin: 10px 0;'>
<h3 style='color: white; margin: 0; text-align: center;'>❌ Video Information Error</h3>
<p style='color: white; margin: 10px 0 0 0; text-align: center;'>
Unable to load video information: {str(e)}
</p>
</div>
"""
def final_analysis_report_llm(video_info, news, classified_comments, keyword_results, opinion_results):
"""Step 4: Final comprehensive analysis report generation in English"""
try:
print("πŸ“ˆ Step 4: Generating final analysis report in English...")
total_comments = len(classified_comments)
positive_count = len([item for item in classified_comments if 'Positive' in item['sentiment_analysis']])
negative_count = len([item for item in classified_comments if 'Negative' in item['sentiment_analysis']])
neutral_count = total_comments - positive_count - negative_count
positive_pct = (positive_count / total_comments * 100) if total_comments > 0 else 0
negative_pct = (negative_count / total_comments * 100) if total_comments > 0 else 0
neutral_pct = (neutral_count / total_comments * 100) if total_comments > 0 else 0
top_comments = sorted(classified_comments, key=lambda x: x['likes'], reverse=True)[:5]
newline = '\n'
top_comments_formatted = newline.join([
f"{i+1}. \"{item['comment']}\" ({item['likes']} likes) - {item['sentiment_analysis'].split('Reason: ')[1] if 'Reason: ' in item['sentiment_analysis'] else 'Analysis provided'}"
for i, item in enumerate(top_comments)
])
final_prompt = f"""
Create a comprehensive YouTube video analysis report in ENGLISH using all the processed data.
VIDEO INFO:
{video_info}
SENTIMENT ANALYSIS RESULTS:
- Total Comments Analyzed: {total_comments}
- Positive: {positive_count} ({positive_pct:.1f}%)
- Negative: {negative_count} ({negative_pct:.1f}%)
- Neutral: {neutral_count} ({neutral_pct:.1f}%)
PUBLIC OPINION ANALYSIS:
{opinion_results}
TOP COMMENTS BY LIKES:
{top_comments_formatted}
Create a detailed analysis report in ENGLISH using the following EXACT format:
# 🎬 YouTube Video Analysis Report
## πŸ“Œ Key Insights
`[Main video topic and focus]`
## 🎯 Video Overview
[Comprehensive summary of video content and context in English]
## πŸ’¬ Comment Sentiment Analysis
### πŸ“Š Sentiment Distribution
- **Positive**: {positive_pct:.1f}% ({positive_count} comments)
- **Negative**: {negative_pct:.1f}% ({negative_count} comments)
- **Neutral**: {neutral_pct:.1f}% ({neutral_count} comments)
### πŸ” Key Comment Insights
1. **Positive Reactions**: [Analysis of positive sentiment patterns in English]
2. **Negative Reactions**: [Analysis of negative sentiment patterns in English]
3. **Core Discussion Topics**: [Main topics and themes from comments in English]
### 🎯 Top Engaged Comments Analysis
[Detailed breakdown of most-liked comments with sentiment explanations in English]
### 🎯 Critical Comments Analysis
[Detailed breakdown of most-negative comments with sentiment explanations in English]
### πŸ‘₯ Public Opinion Summary
[Synthesis of public opinion analysis results in English]
## πŸ“° Content Relevance & Impact
[Analysis of video's relevance to current trends and news in English]
## πŸ’‘ Key Findings
1. **Audience Engagement Pattern**: [Major finding from sentiment analysis in English]
2. **Public Opinion Trend**: [Major finding from opinion analysis in English]
3. **Content Impact Assessment**: [Overall impact and reception analysis in English]
## 🎯 Business Intelligence
### πŸš€ Opportunity Factors
- **Content Strategy**: [Content opportunities based on positive sentiment in English]
- **Audience Engagement**: [Engagement optimization opportunities in English]
- **Brand Positioning**: [Brand opportunities identified from analysis in English]
### ⚠️ Risk Factors
- **Reputation Management**: [Potential risks from negative sentiment in English]
- **Content Concerns**: [Content-related concerns from analysis in English]
- **Audience Feedback**: [Critical feedback points requiring attention in English]
## πŸ“Š Executive Summary
**Bottom Line**: [Two-sentence summary of the analysis and main recommendation in English]
**Key Metrics**: Total Comments: {total_comments} | Engagement Score: [Calculate based on sentiment] |
---
**Analysis Completed**: {datetime.datetime.now()}
**Comments Processed**: {total_comments} | **Analysis Pipeline**: Premium 3-stage LLM process completed
**Report Language**: English | **Data Sources**: YouTube Comments + Video Info + Latest News
"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": final_prompt}],
max_tokens=2000, # Increased for comprehensive English report
temperature=0.5
)
final_report = response.choices[0].message.content.strip()
print(f"βœ… Final English analysis report generated")
return final_report
except Exception as e:
print(f"❌ Final report generation error: {str(e)}")
return f"""# ❌ Analysis Report Generation Failed
## Error Details
**Error**: {str(e)}
**Time**: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
## Status
Analysis completed with {len(classified_comments)} comments processed.
"""
def comment_analyzer(video_id="9P6H2QywDjM", comment_limit=10):
try:
print(f"πŸš€ Starting OPTIMIZED comprehensive analysis for video: {video_id}")
print("πŸ“Š Collecting video data in parallel...")
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
video_info_future = executor.submit(get_youtube_video_info, video_id=video_id)
comments_future = executor.submit(get_youtube_comments, video_id=video_id, limit=comment_limit, order='relevance') # Reduced from 100 to 50
# Get results
video_info, video_info_dict = video_info_future.result()
comments = comments_future.result()
# summarization = summary_future.result()
# video_info, video_info_dict = get_youtube_video_info(video_id)
if video_info == None: return "Check video ID"
# comments = get_youtube_comments(video_id, comment_limit, order="relevance")
# summarization = summarize_video()
sorted_comments = comments.sort_values('likes', ascending=False)
comments_for_analysis = [
{'comment': comment, 'likes': likes}
for comment, likes in zip(sorted_comments['comment'].tolist()[:50], sorted_comments['likes'].tolist()[:50])
]
news = "" # Skip news for speed optimization
print("πŸ€– Starting OPTIMIZED LLM analysis pipeline...")
# Step 1: Sentiment Classification (optimized)
classified_comments = sentiment_classification_llm(comments_for_analysis, comment_limit)
# Step 2: Public Opinion Analysis (optimized)
opinion_results = public_opinion_analysis_llm(classified_comments)
# Step 3: Create Visual Charts in parallel
print("πŸ“Š Creating charts in parallel...")
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
sentiment_future = executor.submit(create_sentiment_pie_chart, classified_comments)
opinion_future = executor.submit(create_public_opinion_bar_chart, opinion_results)
final_report_future = executor.submit(final_analysis_report_llm, video_info, news, classified_comments, "", opinion_results)
sentiment_chart = sentiment_future.result()
opinion_chart = opinion_future.result()
final_report = final_report_future.result()
print("βœ… OPTIMIZED comprehensive analysis complete!")
video_info_markdown = f"""
## πŸ“Ή Video Information
| Video Information |
|------------|
| **🎬 Channel:** {video_info_dict.get('channel_title', 'N/A')[:20]}.. |
| **🎬 Title:** {video_info_dict.get('title', 'N/A')[:20]}.. |
| **πŸ‘€ Views:** {video_info_dict.get('view_count', 'N/A'):,} |
| **πŸ‘ Likes:** {video_info_dict.get('like_count', 'N/A'):,} |
| **πŸ“… Published:** {video_info_dict.get('published_at', 'N/A')} |
"""
return final_report, video_info_markdown, sentiment_chart, opinion_chart
except Exception as e:
print(f"❌ Analysis error: {str(e)}")
error_report = f"# ❌ Analysis Failed\n\nError: {str(e)}\nTime: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
return error_report, None, None