Imageye's picture
Update app.py
8374697 verified
raw
history blame
9.52 kB
import streamlit as st
from youtube_transcript_api import YouTubeTranscriptApi
import re
import tempfile
import os
import openai
# Set up OpenAI API key
openai.api_key = os.getenv("OPENAI_API_KEY")
# Supported file types for OpenAI API
SUPPORTED_FILE_TYPES = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm"]
# Function to transcribe audio using OpenAI Whisper API
def transcribe_audio(file_path):
file_extension = os.path.splitext(file_path)[1][1:]
if file_extension not in SUPPORTED_FILE_TYPES:
return f"Error: Unsupported file type '{file_extension}'. Please upload a valid file."
try:
with open(file_path, "rb") as file:
response = openai.Audio.transcribe(
model="whisper-1",
file=file,
response_format="json"
)
return response['text']
except Exception as e:
return f"Error during transcription: {e}"
# Function to get transcript from YouTube
def get_transcript(url):
try:
video_id_match = re.search(r"(?:v=|\/)([0-9A-Za-z_-]{11}).*", url)
if not video_id_match:
return "Error: Invalid YouTube URL"
video_id = video_id_match.group(1)
transcript = YouTubeTranscriptApi.get_transcript(video_id)
transcript_text = ' '.join([entry['text'] for entry in transcript])
return transcript_text
except Exception as e:
return str(e)
# Function to summarize text using OpenAI API
def summarize_text(text):
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"Summarize the following text:\n\n{text}"}
],
max_tokens=100,
)
summary = response.choices[0].message['content'].strip()
return summary
except Exception as e:
return f"Error summarizing text: {e}"
# Function to generate quiz questions using OpenAI API
def generate_quiz_questions(text):
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"Generate 10 quiz questions from the following text. Make sure the questions cover all key points and are varied in type (e.g., multiple choice, short answer). Number each question and provide the correct answer after the question number:\n\n{text}"}
],
max_tokens=1000,
)
quiz_questions = response.choices[0].message['content'].strip()
return quiz_questions
except Exception as e:
return f"Error generating quiz questions: {e}"
# Function to parse quiz questions from generated text
def parse_quiz_questions(quiz_text):
questions = []
current_question = None
current_choices = []
correct_answer = None
lines = quiz_text.split("\n")
for line in lines:
line = line.strip()
if re.match(r'^\d+\.', line): # This line is a question number
if current_question:
questions.append({
"question": current_question,
"choices": current_choices,
"correct_answer": correct_answer
})
current_question = line
current_choices = []
correct_answer = None
elif line.startswith("Answer:"):
correct_answer = line.split(":", 1)[1].strip()
elif line:
current_choices.append(line)
# Add the last question if it exists
if current_question:
questions.append({
"question": current_question,
"choices": current_choices,
"correct_answer": correct_answer
})
return questions
# Function to generate explanation for quiz answers using OpenAI API
def generate_explanation(question, correct_answer, user_answer):
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"Explain why the correct answer to the following question is '{correct_answer}' and not '{user_answer}':\n\n{question}"}
],
max_tokens=100,
)
explanation = response.choices[0].message['content'].strip()
return explanation
except Exception as e:
return f"Error generating explanation: {e}"
# Function to check answers and provide feedback
def check_answers(questions, user_answers):
feedback = []
correct_count = 0
for i, question in enumerate(questions):
correct_answer = question.get('correct_answer')
user_answer = user_answers.get(f"question_{i+1}", "")
if user_answer == correct_answer:
feedback.append({
"question": question['question'],
"user_answer": user_answer,
"correct_answer": correct_answer,
"status": "Correct"
})
correct_count += 1
else:
explanation = generate_explanation(question['question'], correct_answer, user_answer)
feedback.append({
"question": question['question'],
"user_answer": user_answer,
"correct_answer": correct_answer,
"status": "Incorrect",
"explanation": explanation
})
return feedback
# Function to handle uploaded files
def handle_uploaded_file(uploaded_file):
file_path = tempfile.mktemp(suffix=os.path.splitext(uploaded_file.name)[1])
with open(file_path, "wb") as f:
f.write(uploaded_file.read())
return file_path
# Streamlit app layout and functionality
st.title("YouTube Transcript Quiz Generator")
st.markdown("**Instructions:** Paste a YouTube link or upload a media file to generate a quiz.")
option = st.selectbox("Choose input type", ("YouTube URL", "Upload audio/video file"))
if "generated_quiz" not in st.session_state:
st.session_state.generated_quiz = False
if option == "YouTube URL":
url = st.text_input("YouTube URL", value="")
if url:
if st.button("Generate Quiz"):
transcript_text = get_transcript(url)
if "Error" not in transcript_text:
summary = summarize_text(transcript_text)
quiz_text = generate_quiz_questions(transcript_text)
questions = parse_quiz_questions(quiz_text)
if len(questions) < 10:
st.error("No valid quiz questions could be generated.")
else:
st.session_state.summary = summary
st.session_state.questions = questions[:10] # Ensure only 10 questions are taken
st.session_state.user_answers = {}
st.session_state.generated_quiz = True
else:
st.error(transcript_text)
if option == "Upload audio/video file":
uploaded_file = st.file_uploader("Choose an audio or video file", type=SUPPORTED_FILE_TYPES)
if uploaded_file:
if st.button("Generate Quiz"):
tmp_file_path = handle_uploaded_file(uploaded_file)
transcript_text = transcribe_audio(tmp_file_path)
os.remove(tmp_file_path)
if "Error" not in transcript_text:
summary = summarize_text(transcript_text)
quiz_text = generate_quiz_questions(transcript_text)
questions = parse_quiz_questions(quiz_text)
if len(questions) < 10:
st.error("No valid quiz questions could be generated.")
else:
st.session_state.summary = summary
st.session_state.questions = questions[:10] # Ensure only 10 questions are taken
st.session_state.user_answers = {}
st.session_state.generated_quiz = True
else:
st.error(transcript_text)
if st.session_state.generated_quiz:
st.write("## Summary")
st.write(st.session_state.summary)
st.write("## Quiz Questions")
for i, question in enumerate(st.session_state.questions):
st.write(f"### Question {i+1}")
st.write(question['question'])
st.session_state.user_answers[f"question_{i+1}"] = st.radio(
label="",
options=question['choices'],
key=f"question_{i+1}"
)
if st.button("Submit Answers"):
if "questions" in st.session_state and st.session_state.questions:
with st.spinner('Processing your answers...'):
feedback = check_answers(st.session_state.questions, st.session_state.user_answers)
st.write("## Feedback")
for i, item in enumerate(feedback):
with st.expander(f"Question {i+1} Feedback"):
st.write(f"### {item['question']}")
st.write(f"**Your answer:** {item['user_answer']}")
st.write(f"**Correct answer:** {item['correct_answer']}")
if item['status'] == "Incorrect":
st.write(f"**Explanation:** {item['explanation']}")
else:
st.write("Please generate the quiz first.")