abid-ai commited on
Commit
3f373ed
Β·
verified Β·
1 Parent(s): 29837c0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -79
app.py CHANGED
@@ -7,126 +7,172 @@ from fpdf import FPDF
7
  from youtube_comment_downloader import YoutubeCommentDownloader
8
  import re
9
  import os
10
-
11
- GROQ_API_KEY = os.getenv("translapikey") or os.getenv("GROQ_API_KEY")
12
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  def clean_text(text):
14
  if not text: return ""
15
- text = re.sub(r'[\u2022\u2023\u25CF]', '-', text)
16
- return text.strip()
17
-
18
- def fetch_youtube_comments(url):
 
 
 
 
 
 
 
 
 
 
19
  try:
20
- print(f"Trying to fetch: {url}")
21
-
22
- # Try to extract video ID
23
- match = re.search(r'(?:v=|youtu\.be/|embed/|shorts/)([a-zA-Z0-9_-]+)', url)
24
- if match:
25
- video_id = match.group(1)
26
- url = f"https://www.youtube.com/watch?v={video_id}"
27
-
28
  downloader = YoutubeCommentDownloader()
29
  comments = []
30
- for comment in downloader.get_comments_from_url(url, sort_by=0):
 
 
31
  comments.append(comment['text'])
32
- if len(comments) >= 80:
33
- break
34
- print(f"βœ… Fetched {len(comments)} comments")
35
  return comments
36
  except Exception as e:
37
- print(f"❌ Fetch Error: {str(e)}")
38
  return []
39
 
40
- def analyze_comments_with_groq(comments):
 
41
  try:
42
  client = Groq(api_key=GROQ_API_KEY)
43
- text = "\n\n".join([f"Comment: {clean_text(c)[:200]}" for c in comments[:100]])
44
 
45
  response = client.chat.completions.create(
46
  model="llama-3.3-70b-versatile",
47
  messages=[
48
- {"role": "system", "content": "Analyze these YouTube comments and create a poll with sentiment."},
49
- {"role": "user", "content": text}
50
  ],
51
- temperature=0.3,
52
  response_format={"type": "json_object"}
53
  )
54
  return json.loads(response.choices[0].message.content)
55
  except Exception as e:
56
- print("Groq Error:", str(e))
57
  return None
58
 
59
- def create_pdf_report(result):
60
  pdf = FPDF()
61
  pdf.add_page()
62
  pdf.set_font('Arial', 'B', 16)
63
  pdf.cell(0, 10, 'CommentSurvey AI Report', 0, 1, 'C')
64
  pdf.ln(10)
65
- pdf.set_font('Arial', '', 12)
66
- pdf.multi_cell(0, 8, clean_text(result.get('summary', 'Analysis Completed')))
67
- pdf.output("report.pdf")
68
- return "report.pdf"
 
 
 
 
 
 
 
69
 
 
70
  def analyze(url):
71
  if not GROQ_API_KEY:
72
- return None, "**❌ API Key missing**", None, None, None, None
73
 
74
  comments = fetch_youtube_comments(url)
 
 
75
 
76
- if len(comments) < 5:
77
- return None, """**⚠️ Could not fetch comments automatically.**
78
-
79
- **Reasons & Solutions:**
80
- β€’ Video may have few comments
81
- β€’ Video is age-restricted or private
82
- β€’ YouTube blocked our server
83
-
84
- **Try these public videos:**
85
- β€’ https://youtu.be/dQw4w9WgXcQ
86
- β€’ https://youtu.be/3JZ_3t5fQ8M
87
- β€’ Or paste comments manually (temporary)""", None, None, None, None
88
-
89
  result = analyze_comments_with_groq(comments)
90
-
91
  if not result:
92
- return None, "**❌ AI Analysis failed**", None, None, None, None
93
-
94
- fig_poll = px.pie(names=['Yes', 'No', 'Neutral'], values=[45, 35, 20], hole=0.4)
95
- fig_sent = px.bar(x=['Positive','Negative','Neutral'], y=[50,30,20])
96
-
97
- pdf_path = create_pdf_report(result)
98
 
99
- return result, f"**βœ… Success!** Analyzed {len(comments)} comments", fig_poll, fig_sent, result.get('summary', 'Done'), pdf_path
 
 
 
 
 
100
 
101
- with gr.Blocks(title="CommentSurvey AI", theme=gr.themes.Soft()) as demo:
102
- gr.Markdown("# πŸ“Š CommentSurvey AI\n**YouTube Comments β†’ Smart Survey**")
103
-
104
- url_input = gr.Textbox(
105
- label="🌐 Paste YouTube Video Link",
106
- placeholder="https://youtu.be/dQw4w9WgXcQ",
107
- lines=1
108
- )
109
- analyze_btn = gr.Button("πŸš€ Analyze Comments", variant="primary", size="large")
110
 
111
- status = gr.Markdown("**Status:** Ready")
 
 
 
 
 
112
 
 
 
 
 
 
 
 
 
 
 
113
  with gr.Tabs():
114
- with gr.Tab("πŸ“Š Poll Results"):
115
- poll_plot = gr.Plot()
116
- with gr.Tab("Sentiment"):
117
- sentiment_plot = gr.Plot()
118
- with gr.Tab("Insights"):
119
- insights = gr.Markdown()
120
- with gr.Tab("Raw Comments"):
121
- raw = gr.Dataframe()
122
-
123
- download = gr.File(label="Download PDF")
124
-
125
- analyze_btn.click(
126
- fn=analyze,
127
- inputs=url_input,
128
- outputs=[gr.State(), status, poll_plot, sentiment_plot, insights, download]
129
- )
130
 
131
  if __name__ == "__main__":
132
  demo.launch()
 
7
  from youtube_comment_downloader import YoutubeCommentDownloader
8
  import re
9
  import os
10
+ import warnings
11
+
12
+ warnings.filterwarnings("ignore")
13
+
14
+ # ====================== CONFIG ======================
15
+ # On Hugging Face, set 'GROQ_API_KEY' in the "Variables and Secrets" settings tab
16
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
17
+
18
+ # ====================== SYSTEM PROMPT ======================
19
+ SYSTEM_PROMPT = """
20
+ You are an expert social media sentiment and poll analysis AI.
21
+ Focus on Yes/No, Agree/Disagree, Support/Oppose, and sentiment.
22
+
23
+ Handle English + Urdu + Hindi + other languages well.
24
+ Return ONLY valid JSON in this exact format:
25
+ {
26
+ "main_poll": {
27
+ "question": "Suggested poll question",
28
+ "yes_count": int,
29
+ "no_count": int,
30
+ "agree_count": int,
31
+ "disagree_count": int,
32
+ "support_count": int,
33
+ "oppose_count": int,
34
+ "neutral_count": int
35
+ },
36
+ "sentiment": {
37
+ "positive": float,
38
+ "negative": float,
39
+ "neutral": float
40
+ },
41
+ "top_themes": ["theme1", "theme2"],
42
+ "summary": "Short professional summary",
43
+ "labeled_comments": [
44
+ {"comment": "...", "opinion": "Yes|No|Agree|Disagree|Positive|Negative|Neutral|Mixed"}
45
+ ]
46
+ }
47
+ """
48
+
49
+ # ====================== HELPERS ======================
50
  def clean_text(text):
51
  if not text: return ""
52
+ text = re.sub(r'[\u2022\u2023\u25CF\u25BA\u25C4]', '-', text)
53
+ text = re.sub(r'[\u2018\u2019\u201C\u201D]', '"', text)
54
+ text = re.sub(r'[\u2013\u2014]', '-', text)
55
+ text = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]', '', text)
56
+ return text.encode('latin-1', 'ignore').decode('latin-1') # Ensure PDF compatibility
57
+
58
+ def extract_youtube_id(url):
59
+ patterns = [r'youtu\.be/([a-zA-Z0-9_-]+)', r'v=([a-zA-Z0-9_-]+)', r'/embed/([a-zA-Z0-9_-]+)', r'/shorts/([a-zA-Z0-9_-]+)']
60
+ for p in patterns:
61
+ match = re.search(p, url)
62
+ if match: return match.group(1)
63
+ return None
64
+
65
+ def fetch_youtube_comments(url, limit=100):
66
  try:
67
+ video_id = extract_youtube_id(url)
68
+ if not video_id: return []
 
 
 
 
 
 
69
  downloader = YoutubeCommentDownloader()
70
  comments = []
71
+ # sort_by=0 is "Newest", 1 is "Top"
72
+ gen = downloader.get_comments(video_id, sort_by=1)
73
+ for comment in gen:
74
  comments.append(comment['text'])
75
+ if len(comments) >= limit: break
 
 
76
  return comments
77
  except Exception as e:
78
+ print(f"Fetch error: {e}")
79
  return []
80
 
81
+ def analyze_comments_with_groq(comments, post_context=""):
82
+ if not GROQ_API_KEY: return None
83
  try:
84
  client = Groq(api_key=GROQ_API_KEY)
85
+ comments_text = "\n\n".join([f"C{i+1}: {clean_text(c)[:200]}" for i, c in enumerate(comments)])
86
 
87
  response = client.chat.completions.create(
88
  model="llama-3.3-70b-versatile",
89
  messages=[
90
+ {"role": "system", "content": SYSTEM_PROMPT},
91
+ {"role": "user", "content": f"Context: {post_context}\n\nComments:\n{comments_text}"}
92
  ],
93
+ temperature=0.2,
94
  response_format={"type": "json_object"}
95
  )
96
  return json.loads(response.choices[0].message.content)
97
  except Exception as e:
98
+ print(f"Groq Error: {e}")
99
  return None
100
 
101
+ def create_pdf_report(analysis_result, poll_question):
102
  pdf = FPDF()
103
  pdf.add_page()
104
  pdf.set_font('Arial', 'B', 16)
105
  pdf.cell(0, 10, 'CommentSurvey AI Report', 0, 1, 'C')
106
  pdf.ln(10)
107
+
108
+ pdf.set_font('Arial', 'B', 12)
109
+ pdf.cell(0, 10, f"Question: {poll_question[:60]}", 0, 1, 'L')
110
+
111
+ pdf.set_font('Arial', '', 11)
112
+ summary = analysis_result.get('summary', 'N/A')
113
+ pdf.multi_cell(0, 7, clean_text(summary))
114
+
115
+ path = "report.pdf"
116
+ pdf.output(path)
117
+ return path
118
 
119
+ # ====================== LOGIC ======================
120
  def analyze(url):
121
  if not GROQ_API_KEY:
122
+ return None, "❌ API Key Missing in Hugging Face Secrets", None, None, None, None
123
 
124
  comments = fetch_youtube_comments(url)
125
+ if not comments:
126
+ return None, "❌ Failed to fetch comments.", None, None, None, None
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  result = analyze_comments_with_groq(comments)
 
129
  if not result:
130
+ return None, "❌ AI Analysis failed.", None, None, None, None
 
 
 
 
 
131
 
132
+ main = result.get('main_poll', {})
133
+ poll_values = [
134
+ main.get('yes_count',0) + main.get('agree_count',0) + main.get('support_count',0),
135
+ main.get('no_count',0) + main.get('disagree_count',0) + main.get('oppose_count',0),
136
+ main.get('neutral_count',0)
137
+ ]
138
 
139
+ fig_poll = px.pie(names=['Yes/Agree/Support', 'No/Disagree/Oppose', 'Neutral'],
140
+ values=poll_values, title="Poll Distribution", hole=0.4)
141
+
142
+ sent = result.get('sentiment', {})
143
+ fig_sent = px.bar(x=['Positive', 'Negative', 'Neutral'],
144
+ y=[sent.get('positive',0), sent.get('negative',0), sent.get('neutral',0)],
145
+ title="Sentiment Score", color=['Positive', 'Negative', 'Neutral'])
 
 
146
 
147
+ df = pd.DataFrame(result.get('labeled_comments', []))
148
+ pdf_path = create_pdf_report(result, main.get('question', 'Analysis'))
149
+
150
+ summary_md = f"### πŸ“ {main.get('question', 'Analysis')}\n{result.get('summary', '')}"
151
+
152
+ return df, "βœ… Analysis Complete", fig_poll, fig_sent, summary_md, pdf_path
153
 
154
+ # ====================== UI ======================
155
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
156
+ gr.Markdown("# πŸ“Š CommentSurvey AI")
157
+
158
+ with gr.Row():
159
+ url_input = gr.Textbox(label="YouTube URL", placeholder="https://www.youtube.com/watch?v=...")
160
+ btn = gr.Button("Analyze", variant="primary")
161
+
162
+ status = gr.Markdown("Status: Ready")
163
+
164
  with gr.Tabs():
165
+ with gr.Tab("Summary"):
166
+ sum_md = gr.Markdown()
167
+ with gr.Row():
168
+ p1 = gr.Plot()
169
+ p2 = gr.Plot()
170
+ with gr.Tab("Data"):
171
+ table = gr.Dataframe()
172
+
173
+ report_file = gr.File(label="Download PDF Report")
174
+
175
+ btn.click(analyze, inputs=[url_input], outputs=[table, status, p1, p2, sum_md, report_file])
 
 
 
 
 
176
 
177
  if __name__ == "__main__":
178
  demo.launch()