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"""

šŸ“¹ Video Information

šŸŽ¬ TITLE
{title}
šŸ“ŗ CHANNEL
{channel}
šŸ‘€ VIEWS
{views_formatted}
šŸ‘ LIKES
{likes_formatted}
ā±ļø DURATION
{duration}
šŸ“… PUBLISHED
{published}
šŸŽÆ Video ID: {video_id}
""" return video_info_html except Exception as e: print(f"āŒ Video info display error: {str(e)}") return f"""

āŒ Video Information Error

Unable to load video information: {str(e)}

""" 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