| | |
| | |
| |
|
| | |
| |
|
| |
|
| | |
| |
|
| |
|
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | |
| |
|
| | from urllib.parse import urlparse, parse_qs |
| | import gradio as gr |
| | import requests |
| | from bs4 import BeautifulSoup |
| | import openai |
| | from openai import OpenAI |
| | import speech_recognition as sr |
| | from transformers import pipeline |
| |
|
| | from transformers.pipelines.audio_utils import ffmpeg_read |
| |
|
| | from youtube_transcript_api import YouTubeTranscriptApi, TranscriptsDisabled |
| | from youtube_transcript_api.formatters import TextFormatter |
| |
|
| | from urllib.parse import urlparse, parse_qs |
| | import json |
| |
|
| | import os |
| | import yaml |
| | import pandas as pd |
| | import numpy as np |
| |
|
| | from datetime import datetime, timedelta |
| |
|
| |
|
| | |
| |
|
| | openai_api_key = os.environ["OPENAI_API_KEY"] |
| |
|
| | |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | |
| |
|
| |
|
| | def is_youtube_url(url): |
| | try: |
| | |
| | parsed_url = urlparse(url) |
| | |
| | |
| | if parsed_url.netloc in ["www.youtube.com", "youtube.com", "m.youtube.com", "youtu.be"]: |
| | |
| | if "youtube.com" in parsed_url.netloc: |
| | return "v" in parse_qs(parsed_url.query) |
| | |
| | elif "youtu.be" in parsed_url.netloc: |
| | return len(parsed_url.path.strip("/")) > 0 |
| | return False |
| | except Exception as e: |
| | return False |
| |
|
| | def get_youtube_transcript(youtube_url): |
| | try: |
| | |
| | parsed_url = urlparse(youtube_url) |
| | video_id = parse_qs(parsed_url.query).get("v") |
| | |
| | if not video_id: |
| | return "Invalid YouTube URL. Please provide a valid URL." |
| | |
| | video_id = video_id[0] |
| | |
| | |
| | transcript = YouTubeTranscriptApi.get_transcript(video_id, proxies={"https": "http://localhost:8080"}) |
| |
|
| | |
| | formatter = TextFormatter() |
| | formatted_transcript = formatter.format_transcript(transcript) |
| | |
| | return formatted_transcript |
| | |
| | except Exception as e: |
| | return f"An error occurred: {str(e)}" |
| |
|
| |
|
| | |
| |
|
| |
|
| | def check_subtitles(video_id): |
| | try: |
| | transcripts = YouTubeTranscriptApi.list_transcripts(video_id) |
| | print(f"Available transcripts: {transcripts}") |
| | return True |
| | except TranscriptsDisabled: |
| | print("Subtitles are disabled for this video.") |
| | return False |
| | except Exception as e: |
| | print(f"An unexpected error occurred: {e}") |
| | return False |
| |
|
| | |
| | video_id = "Um017R5Kr3A" |
| | check_subtitles(video_id) |
| |
|
| |
|
| | |
| |
|
| |
|
| | |
| | client = OpenAI(api_key=openai_api_key) |
| |
|
| | |
| |
|
| | |
| | def process_webpage(url): |
| | try: |
| | if is_youtube_url(url): |
| | rendered_content = get_youtube_transcript(url) |
| | else: |
| | |
| | response = requests.get(url) |
| | soup = BeautifulSoup(response.text, "html.parser") |
| | title = soup.title.string.strip() if soup.title else "No Title Found" |
| | |
| | hotlink = f'<a href="{url}" target="_blank" style="color:blue;text-decoration:underline;">{title}</a>' |
| | |
| | html_content = str(soup.prettify()) |
| |
|
| | for script in soup(["script", "style"]): |
| | script.decompose() |
| | rendered_content = soup.get_text(separator="\n").strip().replace("\n\n", "") |
| |
|
| | text_content = rendered_content[:2000] |
| |
|
| | |
| | summary_prompt = f"Summarize the following content:\n{text_content}\n Please use the language of the originial content" |
| | perspectives_prompt = f"Generate a reflective review for the following content:\n{text_content}\n Please output the perspectives in no more than 5 very concise bullet points. Please use the language of the originial content" |
| |
|
| | summary_response = client.chat.completions.create( |
| | model="gpt-4o", |
| | messages=[{"role": "user", "content": summary_prompt}], |
| | max_tokens=500, |
| | ) |
| | perspectives_response = client.chat.completions.create( |
| | model="gpt-4o", |
| | messages=[{"role": "user", "content": perspectives_prompt}], |
| | max_tokens=500, |
| | ) |
| |
|
| | summary = summary_response.choices[0].message.content.strip() |
| | perspectives = perspectives_response.choices[0].message.content.strip() |
| |
|
| | return hotlink, summary, perspectives, rendered_content |
| | except Exception as e: |
| | return f"Error fetching or processing content: {str(e)}", "", "", "" |
| |
|
| |
|
| | |
| |
|
| |
|
| | |
| | def chat_with_ai(chat_history, user_input, content): |
| | try: |
| | messages = [{"role": "system", "content": "You are a helpful assistant."}] |
| | |
| | |
| | for user, bot in chat_history: |
| | messages.append({"role": "user", "content": user}) |
| | messages.append({"role": "assistant", "content": bot}) |
| |
|
| | |
| | messages.append({"role": "user", "content": f"Based on this content: {content}\n\n{user_input}"}) |
| | |
| | |
| | ai_response = client.chat.completions.create( |
| | model="gpt-4o", |
| | messages=messages, |
| | max_tokens=300, |
| | ) |
| | reply = ai_response.choices[0].message.content.strip() |
| | chat_history.append((user_input, reply)) |
| | return chat_history |
| | except Exception as e: |
| | return chat_history + [(user_input, f"Error: {str(e)}")] |
| |
|
| |
|
| | |
| |
|
| |
|
| | def generate_reflection(chat_history): |
| | """ |
| | Generate a reflection based on the chat history. |
| | |
| | Args: |
| | chat_history (list of tuples): List of (user_input, ai_reply) pairs. |
| | |
| | Returns: |
| | str: A reflective summary generated by AI. |
| | """ |
| | try: |
| | messages = [{"role": "system", "content": "You are a professional content summarizer. Generate thoughtful reflections."}] |
| | |
| | |
| | for user, bot in chat_history: |
| | messages.append({"role": "user", "content": user}) |
| | messages.append({"role": "assistant", "content": bot}) |
| | |
| | |
| | messages.append({"role": "user", "content": "Please provide a concise, reflective summary of this conversation."}) |
| | |
| | |
| | ai_response = client.chat.completions.create( |
| | model="gpt-4o", |
| | messages=messages, |
| | max_tokens=200, |
| | ) |
| | reflection = ai_response.choices[0].message.content.strip() |
| | return reflection |
| | except Exception as e: |
| | return f"Error generating reflection: {str(e)}" |
| |
|
| |
|
| | |
| |
|
| |
|
| | import requests |
| |
|
| | def post_to_linkedin(access_token, reflection, visibility="PUBLIC"): |
| | """ |
| | Post a reflection to LinkedIn. |
| | |
| | Args: |
| | access_token (str): LinkedIn API access token. |
| | reflection (str): The content to post. |
| | visibility (str): Visibility setting ("PUBLIC" or "CONNECTIONS"). Defaults to "PUBLIC". |
| | |
| | Returns: |
| | str: Confirmation or error message. |
| | """ |
| | try: |
| | url = "https://api.linkedin.com/v2/ugcPosts" |
| | headers = { |
| | "Authorization": f"Bearer {access_token}", |
| | "Content-Type": "application/json", |
| | } |
| | your_linkedin_person_id = 'jay' |
| | payload = { |
| | "author": f"urn:li:person:{your_linkedin_person_id}", |
| | "lifecycleState": "PUBLISHED", |
| | "visibility": {"com.linkedin.ugc.MemberNetworkVisibility": visibility}, |
| | "specificContent": { |
| | "com.linkedin.ugc.ShareContent": { |
| | "shareCommentary": { |
| | "text": reflection |
| | }, |
| | "shareMediaCategory": "NONE" |
| | } |
| | } |
| | } |
| | |
| | response = requests.post(url, headers=headers, json=payload) |
| | if response.status_code == 201: |
| | return "Reflection successfully posted to LinkedIn!" |
| | else: |
| | return f"Failed to post to LinkedIn. Error: {response.json()}" |
| | except Exception as e: |
| | return f"Error posting to LinkedIn: {str(e)}" |
| |
|
| | |
| | copy_to_clipboard_js = """ |
| | function copyToClipboard(text) { |
| | navigator.clipboard.writeText(text).then(() => { |
| | alert("Text copied to clipboard!"); |
| | }).catch(err => { |
| | alert("Failed to copy text: " + err); |
| | }); |
| | } |
| | """ |
| |
|
| | |
| |
|
| |
|
| | |
| | with gr.Blocks() as demo: |
| | gr.Markdown("## Curify Digest: Consume and interact with content") |
| |
|
| | with gr.Row(): |
| | with gr.Column(): |
| | gr.Markdown("## Render Webpage") |
| | url_input = gr.Textbox(label="Enter URL") |
| | with gr.Column(): |
| | |
| | fetch_btn = gr.Button("Fetch and Process Webpage") |
| | title_output = gr.HTML(label="Webpage Content") |
| | |
| | with gr.Row(): |
| | |
| | with gr.Column(): |
| | gr.Markdown("## Summary and perspectives") |
| | summary_output = gr.Textbox(label="Summary", lines=5) |
| | perspectives_output = gr.Textbox(label="Perspectives", lines=5) |
| | fulltext_output = gr.Textbox(label="Fulltext", visible=False) |
| | |
| | with gr.Column(): |
| | gr.Markdown("## Interactive Chatbot and reflections") |
| | chatbot_history_gr = gr.Chatbot(label="Chat History") |
| | user_input = gr.Textbox(label="Ask a Question", placeholder="Type your question here...") |
| | chatbot_btn = gr.Button("Chat") |
| | |
| | with gr.Column(): |
| | reflection_btn = gr.Button("Generate reflection") |
| | reflection_output = gr.Textbox(label="Reflections",interactive=True, lines=5) |
| | |
| | custom_js = """ |
| | <script> |
| | function copyToClipboard() { |
| | const textbox = document.querySelector("textarea[aria-label='Reflections']"); |
| | if (textbox) { |
| | navigator.clipboard.writeText(textbox.value).then(() => { |
| | alert("Text copied to clipboard!"); |
| | }).catch(err => { |
| | alert("Failed to copy text: " + err); |
| | }); |
| | } |
| | } |
| | </script> |
| | <button onclick="copyToClipboard()">Copy to clipboard</button> |
| | """ |
| | gr.HTML(custom_js) |
| | |
| | fetch_btn.click( |
| | process_webpage, |
| | inputs=url_input, |
| | outputs=[title_output, summary_output, perspectives_output, fulltext_output], |
| | ) |
| | |
| | chatbot_btn.click( |
| | chat_with_ai, |
| | inputs=[chatbot_history_gr, user_input, fulltext_output], |
| | outputs=chatbot_history_gr, |
| | ) |
| |
|
| | reflection_btn.click( |
| | generate_reflection, |
| | inputs=chatbot_history_gr, |
| | outputs=reflection_output, |
| | ) |
| |
|
| | demo.launch(share=True) |
| |
|
| |
|
| | |