Spaces:
Runtime error
Runtime error
| def plot_sentiment_pie(results, title="Reels Sentiment Analysis"): | |
| """ | |
| Creates a pie chart from sentiment analysis results and returns the matplotlib figure. | |
| Args: | |
| results: Counter object or dict with 'positive', 'neutral', 'negative' keys | |
| title: Chart title | |
| Returns: | |
| Matplotlib Figure object, or None if no data. | |
| """ | |
| labels = ['Positive', 'Neutral', 'Negative'] | |
| sizes = [results.get('positive', 0), results.get('neutral', 0), results.get('negative', 0)] | |
| if sum(sizes) == 0: | |
| return None | |
| colors = ['#4CAF50', '#FFC107', '#F44336'] | |
| explode = (0.05, 0, 0.05) | |
| fig, ax = plt.subplots(figsize=(8, 6)) | |
| filtered_labels = [label for i, label in enumerate(labels) if sizes[i] > 0] | |
| filtered_sizes = [size for size in sizes if size > 0] | |
| filtered_colors = [colors[i] for i, size in enumerate(sizes) if size > 0] | |
| explode_map = {'Positive': 0.05, 'Neutral': 0, 'Negative': 0.05} | |
| filtered_explode = [explode_map.get(label, 0) for label in filtered_labels] | |
| ax.pie(filtered_sizes, explode=filtered_explode, labels=filtered_labels, colors=filtered_colors, | |
| autopct='%1.1f%%', shadow=True, startangle=140, | |
| textprops={'fontsize': 12, 'color': 'black'}) | |
| ax.axis('equal') | |
| plt.title(title, fontsize=16, pad=20) | |
| plt.tight_layout() | |
| # Return the figure object instead of saving to bytes | |
| return fig | |
| def plot_category_distribution(counter, title="Reels Content Distribution"): | |
| """ | |
| Generate pie chart from category counts and returns the matplotlib figure. | |
| Args: | |
| counter: Counter object with category counts. | |
| title: Chart title. | |
| Returns: | |
| Matplotlib Figure object, or None if no data. | |
| """ | |
| labels = [] | |
| sizes = [] | |
| total = sum(counter.values()) | |
| if total == 0: | |
| return None | |
| threshold = total * 0.02 | |
| other_count = 0 | |
| sorted_categories = counter.most_common() | |
| for category, count in sorted_categories: | |
| if count >= threshold and category != "other": | |
| labels.append(category.replace('_', ' ').title()) | |
| sizes.append(count) | |
| elif category == "other": | |
| other_count += count | |
| else: | |
| other_count += count | |
| if other_count > 0: | |
| labels.append("Other") | |
| sizes.append(other_count) | |
| if not sizes: | |
| return None | |
| fig, ax = plt.subplots(figsize=(10, 8)) | |
| colors = plt.cm.viridis(np.linspace(0, 1, len(sizes))) | |
| ax.pie( | |
| sizes, | |
| labels=labels, | |
| autopct='%1.1f%%', | |
| startangle=140, | |
| colors=colors, | |
| wedgeprops={'edgecolor': 'white', 'linewidth': 1}, | |
| textprops={'fontsize': 11, 'color': 'black'} | |
| ) | |
| plt.title(title, pad=20, fontsize=15) | |
| plt.axis('equal') | |
| plt.tight_layout() | |
| # Return the figure object instead of saving to bytes | |
| return fig | |
| # The rest of the Gradio Blocks interface definition and function linking | |
| # should remain the same, as the analyze_reels_gradio function already | |
| # calls these plotting functions and is intended to return the figure objects now. | |
| # Global variables to maintain state across Gradio calls | |
| global cl | |
| global explore_reels_list | |
| global sentiment_analyzer_instance | |
| global content_classifier_pipeline | |
| # Initialize sentiment analyzer if not already done (can be done here or lazily in analyze_reels_gradio) | |
| # Doing it here ensures the model is loaded when this cell runs, potentially reducing latency on first analyze click. | |
| try: | |
| sentiment_analyzer_instance = ReelSentimentAnalyzer() | |
| print("Sentiment Analyzer initialized.") | |
| # Optional: Train Hindi model if needed and data is available | |
| # sample_train_data = [...] # Define your training data | |
| # sentiment_analyzer_instance.train_hindi_model(sample_train_data) | |
| except Exception as e: | |
| print(f"Error initializing Sentiment Analyzer globally: {e}") | |
| sentiment_analyzer_instance = None | |
| # Initialize content classifier pipeline if not already done (can be done here or lazily) | |
| try: | |
| print("Initializing Content Classifier Pipeline globally...") | |
| content_classifier_pipeline = pipeline( | |
| "zero-shot-classification", | |
| model="facebook/bart-large-mnli", | |
| device=0 if torch.cuda.is_available() else -1 # Use GPU if available | |
| ) | |
| print("Content Classifier Pipeline Initialized.") | |
| except Exception as e: | |
| print(f"Error initializing Content Classifier globally: {e}") | |
| content_classifier_pipeline = None | |
| def login_gradio(username): | |
| """Gradio-compatible login function.""" | |
| global cl | |
| try: | |
| PASSWORD = userdata.get('password') | |
| except Exception as e: | |
| return f"Error accessing password secret: {e}" | |
| if not PASSWORD: | |
| return "Error: Instagram password not found in Colab secrets." | |
| cl = Client() | |
| try: | |
| cl.login(username, PASSWORD) | |
| return f"Successfully logged in as {username}" | |
| except Exception as e: | |
| cl = None # Ensure cl is None on failure | |
| return f"Error during login: {e}" | |
| def fetch_reels_gradio(): | |
| """Gradio-compatible function to fetch explore reels.""" | |
| global cl | |
| global explore_reels_list | |
| if cl is None: | |
| explore_reels_list = [] # Ensure list is empty on failure | |
| return "Error: Not logged in. Please log in first." | |
| try: | |
| # Fetch a limited number of reels for demonstration purposes | |
| # You might want to make this number configurable later | |
| fetched_reels = cl.explore_reels()[:100] # Fetch up to 100 for analysis | |
| explore_reels_list = fetched_reels | |
| if explore_reels_list: | |
| return f"Successfully fetched {len(explore_reels_list)} explore reels." | |
| else: | |
| explore_reels_list = [] # Ensure it's an empty list | |
| return "Fetched 0 explore reels." | |
| except Exception as e: | |
| explore_reels_list = [] # Ensure it's an empty list on error | |
| return f"Error fetching explore reels: {e}" | |
| def analyze_reels_gradio(max_to_analyze): | |
| """Gradio-compatible function to analyze fetched reels and generate plots.""" | |
| global explore_reels_list | |
| global sentiment_analyzer_instance | |
| global content_classifier_pipeline | |
| if not explore_reels_list: | |
| # Return None for plots if no reels | |
| return "Error: No reels fetched yet. Please fetch reels first.", None, None | |
| # Ensure max_to_analyze does not exceed the number of fetched reels | |
| num_reels_to_process = min(max_to_analyze, len(explore_reels_list)) | |
| reels_to_analyze = explore_reels_list[:num_reels_to_process] | |
| if not reels_to_analyze: | |
| return "Error: No reels available to analyze.", None, None | |
| # Check if analyzers are initialized | |
| if sentiment_analyzer_instance is None: | |
| return "Error: Sentiment Analyzer not initialized.", None, None | |
| if content_classifier_pipeline is None: | |
| return "Error: Content Classifier not initialized.", None, None | |
| analysis_status_messages = [] | |
| sentiment_plot_figure = None # Changed to figure | |
| content_plot_figure = None # Changed to figure | |
| # Perform Sentiment Analysis | |
| try: | |
| analysis_status_messages.append(f"Starting Sentiment Analysis for {len(reels_to_analyze)} reels...") | |
| sentiment_results, detailed_sentiment_results = sentiment_analyzer_instance.analyze_reels( | |
| reels_to_analyze, | |
| max_to_analyze=len(reels_to_analyze) # Pass the actual number being processed | |
| ) | |
| # Call the updated plotting function that returns a figure | |
| sentiment_plot_figure = plot_sentiment_pie(sentiment_results, title=f"Sentiment of {len(reels_to_analyze)} Instagram Reels") | |
| analysis_status_messages.append("Sentiment Analysis Complete.") | |
| except Exception as e: | |
| analysis_status_messages.append(f"Error during Sentiment Analysis: {e}") | |
| sentiment_plot_figure = None # Ensure plot is None on error | |
| # Perform Content Categorization | |
| try: | |
| analysis_status_messages.append(f"Starting Content Categorization for {len(reels_to_analyze)} reels...") | |
| category_counts = Counter() | |
| # Re-implement content analysis slightly to fit this flow using the global pipeline | |
| print(f"\n⏳ Analyzing content for {len(reels_to_analyze)} reels...") | |
| for i, reel in enumerate(reels_to_analyze, 1): | |
| caption = getattr(reel, 'caption_text', '') or getattr(reel, 'caption', '') or '' | |
| # Use the global classifier pipeline | |
| category, details = classify_reel_content(caption) | |
| category_counts[category] += 1 | |
| print("\n✅ Content Analysis complete!") | |
| print("\n📊 Category Counts:") | |
| for category, count in category_counts.most_common(): | |
| print(f"- {category.replace('_', ' ').title()}: {count}") | |
| # Call the updated plotting function that returns a figure | |
| content_plot_figure = plot_category_distribution(category_counts) | |
| analysis_status_messages.append("Content Categorization Complete.") | |
| except Exception as e: | |
| analysis_status_messages.append(f"Error during Content Analysis: {e}") | |
| content_plot_figure = None # Ensure plot is None on error | |
| final_status_message = "\n".join(analysis_status_messages) | |
| # Return the figure objects | |
| return final_status_message, sentiment_plot_figure, content_plot_figure | |
| # --- Gradio Blocks Interface --- | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Instagram Reels Analysis") | |
| with gr.Row(): | |
| username_input = gr.Textbox(label="Instagram Username") | |
| login_button = gr.Button("Login") | |
| login_status_output = gr.Label(label="Login Status") | |
| with gr.Row(): | |
| fetch_button = gr.Button("Fetch Reels") | |
| fetch_status_output = gr.Label(label="Fetch Status") | |
| with gr.Row(): | |
| max_reels_input = gr.Slider(minimum=1, maximum=100, value=10, step=1, label="Number of Reels to Analyze") | |
| analyze_button = gr.Button("Analyze Reels") | |
| analyze_status_output = gr.Label(label="Analysis Status") | |
| with gr.Row(): | |
| # Sentiment Analysis Outputs | |
| with gr.Column(): | |
| gr.Markdown("## Sentiment Analysis") | |
| sentiment_plot_output = gr.Plot(label="Sentiment Distribution") | |
| # Content Analysis Outputs | |
| with gr.Column(): | |
| gr.Markdown("## Content Analysis") | |
| content_plot_output = gr.Plot(label="Content Distribution") | |
| # Link login and fetch buttons (assuming login_gradio and fetch_reels_gradio are defined) | |
| # Redefine login_gradio and fetch_reels_gradio here within the Blocks context | |
| # to ensure they are linked correctly, even though they were defined above. | |
| # This is a common pattern in Gradio Blocks. | |
| def login_gradio_blocks(username): | |
| """Gradio-compatible login function for Blocks.""" | |
| global cl | |
| try: | |
| PASSWORD = userdata.get('password') | |
| except Exception as e: | |
| return f"Error accessing password secret: {e}" | |
| if not PASSWORD: | |
| return "Error: Instagram password not found in Colab secrets." | |
| cl = Client() | |
| try: | |
| cl.login(username, PASSWORD) | |
| return f"Successfully logged in as {username}" | |
| except Exception as e: | |
| cl = None # Ensure cl is None on failure | |
| return f"Error during login: {e}" | |
| def fetch_reels_gradio_blocks(): | |
| """Gradio-compatible function to fetch explore reels for Blocks.""" | |
| global cl | |
| global explore_reels_list | |
| if cl is None: | |
| explore_reels_list = [] # Ensure list is empty on failure | |
| return "Error: Not logged in. Please log in first." | |
| try: | |
| # Fetch a limited number of reels for demonstration purposes | |
| # You might want to make this number configurable later | |
| fetched_reels = cl.explore_reels()[:100] # Fetch up to 100 for analysis | |
| explore_reels_list = fetched_reels | |
| if explore_reels_list: | |
| return f"Successfully fetched {len(explore_reels_list)} explore reels." | |
| else: | |
| explore_reels_list = [] # Ensure it's an empty list | |
| return "Fetched 0 explore reels." | |
| except Exception as e: | |
| explore_reels_list = [] # Ensure it's an empty list on error | |
| return f"Error fetching explore reels: {e}" | |
| login_button.click( | |
| fn=login_gradio_blocks, | |
| inputs=username_input, | |
| outputs=login_status_output | |
| ) | |
| fetch_button.click( | |
| fn=fetch_reels_gradio_blocks, | |
| inputs=None, # No direct inputs needed for fetching | |
| outputs=fetch_status_output | |
| ) | |
| # Link the Analyze button to the analysis function | |
| analyze_button.click( | |
| fn=analyze_reels_gradio, | |
| inputs=max_reels_input, # Input is the slider value | |
| outputs=[analyze_status_output, sentiment_plot_output, content_plot_output] # Outputs are status and the two plots | |
| ) | |
| # The demo is now fully defined. It can be launched in the next step. | |
| # demo.launch() | |