Author
Hrishikesh Yadav
Date Published
October 4, 2024
Tags
API Tutorial
Applications
Developers
Generate API
Generative AI
Open Source
Share
Join our newsletter
You’re now subscribed to the Twelve Labs Newsletter! You'll be getting the latest news and updates in video understanding.
Oh no, something went wrong.
Please try again.

Introduction

Imagine a world where every video becomes an interactive quiz, effortlessly transforming passive viewing into active learning πŸŽ“

In this tutorial, we'll explore the Video Content Quiz Generator, an application designed to revolutionize the learning experience associated with video content. Powered by Twelve Labs, this video-based tool automatically generates Multiple Choice Questions (MCQs) from video content, enhancing both learning and assessment.

Whether you're an educator seeking quick assessments or a content creator aiming to boost engagement, the Video Content MCQ Generator is your ally in fostering active learning.

You can explore the demo of the application here: Video Content MCQ Generator. You can play with it via this Replit template.

‍

Prerequisites

How the Application Works

This section outlines the application flow for developing the video content quiz (MCQ) generator using Twelve Labs.

The process begins when a user uploads video or educational content, which is then transformed into an active quiz assessment. After upload, the video is indexed on a specific index created during setup. Indexing is performed using Marengo 2.6 (Embedding Engine). Once indexed, the video content is converted into MCQs using the open-ended prompt functionality in Pegasus 1.1 (Generative Engine).

The prompt assigns the role of an educational video analyzer, tasked with generating quiz questions and correct answers. These are used to evaluate user responses based on the indexed video, following a specified format. After a user submits an answer, the application provides a score and the correct answer. A reset button allows users to start over.

Preparation Steps

There are two ways to create the index: through the Twelve Labs Playground or using provided code snippets. We'll discuss both methods. To prepare the index ID from the playground, follow these steps:

Twelvelabs_API=your_api_key_here
API_URL=your_api_url_here

If you prefer the code-based approach, follow these steps:

  • Obtain your API Key from the Twelve Labs Playground and prepare the environment variable.
  • Import the Twelve Labs SDK and the environmental variables. Initiate the SDK client using the Twelve Labs API Key from the environment variable.
from twelvelabs import TwelveLabs
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv("API_KEY")
client = TwelveLabs(api_key=API_KEY)
  • Specifying the desired engine for the generation task:
engines = [
        {
          "name": "marengo2.6",
          "options": ["visual", "conversation", "text_in_video", "logo"]
        },
        {
            "name": "pegasus1.1",
            "options": ["visual", "conversation"]
        }
    ]
  • Create a new index by calling client.index with the Index name and engine configuration parameters. Use a unique and identifiable name for the Index.
index = client.index.create(
    name="<YOUR_INDEX_NAME>",
    engines=engines
)
print(f"A new index has been created: Index id={index.id} name={index.name} engines={index.engines}")

The index.id field represents the unique identifier of your new index. This identifier is crucial for indexing videos in their correct location.

With these steps completed, you're now ready to develop the application!

‍

Walkthrough for Video Content Quiz Generator

In this tutorial, we will build a Streamit application with a minimal frontend. Below is the directory structure to be followed:

.
β”œβ”€β”€ app.py
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ utils.py
β”œβ”€β”€ .env
└── .gitignore

After completing the previous steps, it's time to build the Streamlit application. This app offers a straightforward way to upload a video and convert it into a multiple-choice quiz assessment. The application comprises two files:

  • app.py: Contains the application flow with a minimal page layout
  • utils.py: Contains all the utility functions essential for the application's operation

‍

1 - Setting up the utility function for the operation

In this section, we'll explore how to set up the utility function using the Twelve Labs SDK to transform video content into interactive learning experiences. This AI-powered system enables educators and content creators to craft engaging assessments based on visual content by automating video analysis and quiz generation. The code combines video indexing and custom prompt engineering to generate relevant multiple-choice questions from any uploaded video, making it a versatile tool for creating interactive learning materials.

# Import the necessary libraries
import json
import tempfile
import os
from twelvelabs import TwelveLabs
from twelvelabs.models.task import Task
import re
from dotenv import load_dotenv


load_dotenv()

# Get the API Key from the Dashboard - https://playground.twelvelabs.io/dashboard/api-key
API_KEY = os.getenv("API_KEY")

# Create the INDEX ID as specified in the README.md and get the INDEX_ID
INDEX_ID = os.getenv("INDEX_ID")

# Initialize the Twelve Labs client
client = TwelveLabs(api_key=API_KEY)

# Create a temporary directory for uploaded files
UPLOAD_DIR = tempfile.mkdtemp()

def create_task(file_path):

    # Create a new task for video indexing
    return client.task.create(
        index_id=INDEX_ID,
        file=file_path,
    )

def on_task_update(task: Task):
    # Callback function to print task status updates
    print(f" Status={task.status}")

def wait_for_task(task):

    # Wait for the indexing task to complete
    task.wait_for_done(sleep_interval=5, callback=on_task_update)
    if task.status != "ready":
        raise RuntimeError(f"Indexing failed with status {task.status}")
    return task.video_id

def generate_mcq(video_id):
    # Prompt to generate the multiple choice questions based on video content
    prompt = """You're Educational Content Analyzer, and you are tasked to prepare the three Multiple Choice Questions based on the video content and the concept which is been discussed by the speaker or shown. The difficulty of the question should also gradually increase. 

    The response should be in the json format where there is Q1, Q2, and Q3. Each section would contain the question with options in question and the correct_answer"""
    

    # Twelve Labs SDK to generate text based on the video content
    gist_r = client.generate.text(
        video_id=video_id,
        prompt=prompt
    )
    return gist_r.data

def parse_json_with_regex(text):

    # Extract and parse JSON content from the response
    match = re.search(r'\{[\s\S]*\}', text)

    if match:
        json_str = match.group(0)
        try:
            return json.loads(json_str)
        except json.JSONDecodeError:

            # Return None if JSON parsing fails
            return None
    else:
        # Return None if no JSON like content is found
        return None

def save_uploaded_file(uploaded_file):

    # Save the uploaded file to the temporary directory
    file_path = os.path.join(UPLOAD_DIR, uploaded_file.name)
    with open(file_path, "wb") as f:
        f.write(uploaded_file.getbuffer())
    return file_path


# Calculate the user's score based on their answers
def calculate_score(user_answers, questions):
    return sum(answer == questions[q_num]["correct_answer"] for q_num, answer in user_answers.items())

First, we import the necessary modules, including json, tempfile, os, and the Twelve Labs SDK, along with the API Key and Index ID environment variables.

  1. The create_task(file_path) method initiates a new video indexing task using the Twelve Labs SDK. It returns the work object for the indexing application. A callback function, on_task_update(task: Task), prints status updates for the indexing task. Once the status becomes "ready" and returns the video_id, the generate_mcq(video_id) function is triggered.
  2. The generate_mcq(video_id) function generates multiple-choice questions based on the indexed content using Pegasus 1.1. The prompt defines the model's role, objective, and output format. The result is then parsed using parse_json_with_regex(text).
  3. The calculate_score(user_answers, questions) function computes the user's score based on their answers and returns the total score when the user clicks the submit button in the next section.

‍

2. Instruction Flow of the Streamlit Application [app.py]
2.1 Quiz Assessment Instruction Flow

As mentioned earlier, app.py contains the proper flow of instructions for the Streamlit application, utilizing the utility functions from utils.py. This section focuses on the function and flow of the quiz assessment and scoring system.

# Import the necessary libraries and functions
import streamlit as st
from utils import create_task, wait_for_task, generate_mcq, parse_json_with_regex, save_uploaded_file, calculate_score

def upload_and_index():

    # Minimal custom HTML and CSS
    st.markdown("""
        <h1 style='text-align: center; color: #2c3e50; font-size: 36px; font-weight: bold; margin-bottom: 30px;'>
            πŸ“Ή Video Content Quiz Generator
        </h1>
    """, unsafe_allow_html=True)

    st.write("Upload a video to start the quiz generation process and test yourself!")

    # # Check if a video has already been indexed
    if 'video_id' in st.session_state and st.session_state.video_id:
        st.info("Video already indexed. Next, Proceeding to quiz generation.")
        return generate_quiz()


    # File uploader for video files
    uploaded_file = st.file_uploader("Choose a video file", type=['mp4'])

    if uploaded_file is not None:

        # Save the uploaded file
        file_path = save_uploaded_file(uploaded_file)
        st.success("File uploaded successfully!")

        # To proceed witht the indexing of the video
        with st.spinner("Indexing video... Please wait"):
            task = create_task(file_path)
            video_id = wait_for_task(task)

        st.success("Video indexed successfully!")
        st.session_state.video_id = video_id
        
        return generate_quiz()

# Utility function to generate the quiz questions based on the indexed video
def generate_quiz():

    with st.spinner("Generating quiz questions..."):
        raw_response = generate_mcq(st.session_state.video_id)
        questions = parse_json_with_regex(raw_response)

    if questions:
        st.session_state.questions = questions
        st.session_state.page = "quiz"
        st.experimental_rerun()
    else:
        st.error("Failed to generate quiz questions. Please try again.")

def quiz():
    st.title("πŸŽ“ Video Content Quiz")
    st.write("Answer the following questions based on the video content -")

    questions = st.session_state.questions
    

    # # Initialize user answers if not already done
    if 'user_answers' not in st.session_state:
        st.session_state.user_answers = {q_num: None for q_num in questions}
    if 'submitted' not in st.session_state:
        st.session_state.submitted = False


    # Display the questions and answer options
    for q_num, q_data in questions.items():
        with st.container():
            st.subheader(f"Question {q_num[-1]}")
            st.write(q_data["question"])
            
            answer = st.radio(
                f"Select your answer for Question {q_num[-1]}:",
                options=q_data["options"],
                key=f"select_{q_num}",
                index=q_data["options"].index(st.session_state.user_answers[q_num]) if st.session_state.user_answers[q_num] else 0,
                on_change=update_answer,
                args=(q_num,)
            )
        st.markdown("---")

    # Submit quiz button
    col1, col2, col3 = st.columns([1, 2, 1])
    with col2:
        submit_button = st.button("Submit Quiz πŸ“", key="submit_quiz", use_container_width=True)
        if submit_button:
            submit_quiz()
            st.balloons()

    # Display results if quiz is submitted
    if st.session_state.submitted:
        display_results()

    start_over_button = st.button("Start Over πŸ”„", key="start_over", use_container_width=True)
    if start_over_button:
        reset_state()

def update_answer(q_num):
    # Session state, to update the user's answer
    st.session_state.user_answers[q_num] = st.session_state[f"select_{q_num}"]

def submit_quiz():
    # On the submission button, mark quiz as submitted and calculate score
    st.session_state.submitted = True
    st.session_state.score = calculate_score(st.session_state.user_answers, st.session_state.questions)

def display_results():
    # To display the user's score
    st.success(f"πŸ† Your score: {st.session_state.score} out of {len(st.session_state.questions)}")

    # Display the correct results for each question
    for q_num, q_data in st.session_state.questions.items():
        with st.expander(f"Question {q_num[-1]} Details"):
            st.write(f"**Your answer:** {st.session_state.user_answers[q_num]}")
            st.write(f"**Correct answer:** {q_data['correct_answer']}")
            if st.session_state.user_answers[q_num] == q_data['correct_answer']:
                st.success("Correct! πŸŽ‰")
            else:
                st.error("Incorrect ❌")

def reset_state():
    # Clear all session state variables
    for key in list(st.session_state.keys()):
        del st.session_state[key]
    st.experimental_rerun()

This part of the Streamlit application focuses on generating quizzes from video content and managing quiz interactions.

The upload_and_index() function utilizes the Twelve Labs SDK for video uploading and indexing. Once indexing is complete, the quiz generation process begins.

To generate quiz questions, the function uses the 'video_id' of the indexed video. It then calls generate_mcq() followed by parse_json_with_regex(), as previously discussed. This process creates the Quiz Questions and Answers. Upon successful generation, the quiz page is displayed.

The quiz() function presents the generated quiz and handles user interactions, including answer selection and quiz submission. A "Start Over" option is provided after submission. Several utility functions manage quiz interactions:

  • update_answer() updates the user's response for a specific question.
  • submit_quiz() processes quiz submission and calculates the score.
  • display_results() shows the quiz results, including the score and correct answers.
  • reset_state() clears all session state variables to restart the quiz.

‍

2.2 Streamlit Session State and Custom CSS

The main function in the app.py package configures the Streamlit page and applies custom CSS. It sets the background image, styles buttons, and determines the overall look of the application. It also initializes the session state.

def main():
    # Set up the Streamlit page configuration
    st.set_page_config(page_title="QnA Generator", page_icon="πŸŽ₯", layout="wide")

    # Custom CSS
    st.markdown("""
        <style>
        .stApp {
            background-color: #f0f2f6;
        }
        .stButton > button {
            background-color: #4CAF50;
            color: white;
            font-weight: bold;
            border-radius: 30px;
            padding: 15px 30px;
            font-size: 18px;
            transition: all 0.3s ease 0s;
            border: none;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        .stButton > button:hover {
            background-color: #45a049;
            box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
            transform: translateY(-2px);
        }
        .stButton > button:active {
            transform: translateY(0px);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
        .stRadio > label {
            background-color: #e1e5eb;
            padding: 12px;
            border-radius: 8px;
            margin-bottom: 12px;
            transition: all 0.2s ease 0s;
            cursor: pointer;
        }
        .stRadio > label:hover {
            background-color: #d0d4d9;
            transform: translateY(-2px);
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        .stExpander {
            background-color: #ffffff;
            border-radius: 15px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            margin-bottom: 20px;
        }
        h1 {
            color: #2c3e50;
            font-size: 36px;
            font-weight: bold;
            margin-bottom: 20px;
        }
        h2, h3 {
            color: #34495e;
        }
        .stAlert {
            border-radius: 10px;
            font-weight: bold;
        }
        .stSpinner > div {
            border-color: #4CAF50 !important;
        }
        [data-testid="stAppViewContainer"] {
            background-image: url("https://img.freepik.com/free-photo/vivid-blurred-colorful-wallpaper-background_58702-3508.jpg?size=626&ext=jpg");
            background-size: cover;
        }
        [data-testid="stHeader"] {
            background-color: rgba(0,0,0,0);
        }
        [data-testid="stToolbar"] {
            right: 2rem;
            background-image: url("");
            background-size: cover;
        }
        </style>
    """, unsafe_allow_html=True)


    # Initialize the page state if not already set
    if 'page' not in st.session_state:
        st.session_state.page = "upload"


    # To show the appropriate page based on the current state
    if st.session_state.page == "upload":
        upload_and_index()
    elif st.session_state.page == "quiz":
        quiz()

if __name__ == "__main__":
    main()

Serving as the entry point for launching the Streamlit application, the main() function sets up a page with a title and uses a wide layout for better space utilization. Custom CSS enhances visual appeal by stylizing the background, buttons, radio buttons, and expanders, while reducing the opacity of headings.

To manage navigation between different sections of the app, a session state variable 'page' is initialized. It's set to "upload" by default if not already defined.

The application renders various pages based on its current state using conditional statements:

  • "upload" β€” Calls the upload_and_index() function for the video upload interface.
  • "quiz" β€” Calls the quiz() function to display the generated quiz.

Here's a demo quiz generated after uploading and indexing the video content on the PCA topic:

Explore further by experimenting with the open-ended prompt and Pegasus 1.1, powered by Twelve Labs.

‍

More Ideas to Experiment with the Tutorial

Understanding how an application works and its development process empowers you to implement innovative ideas and create products that meet users' needs. Here are some use cases similar to the tutorial blog:

πŸ“šοΈ Self-Paced Learning: Students can generate questions to test their understanding of video lessons.

πŸŽ“ Educational Assessment: Teachers can quickly create quizzes based on lecture videos or educational content.

🏒️ Corporate Training: Organizations can enhance their video-based training programs with automated assessments tailored to different learners.

🎬 Content Engagement: Video content creators, including YouTubers, can increase engagement by providing interactive quizzes for their content.

🧠 Memory Retention: Users can reinforce their learning by answering questions generated from informational videos.

‍

Conclusion

This blog post offers a comprehensive explanation of the working procedure and development of the video content quiz generator using Twelve Labs. We appreciate you following along with the tutorial and look forward to your ideas on improving the user experience and addressing various challenges.

‍

Additional Resources

To deepen your understanding of the engines used for the generation task, check out Marengo 2.6 (Embedding Engine) and Pegasus 1.1 (Generator Engine). To further explore Twelve Labs and enhance your grasp of video content analysis, consider these valuable resources:

  • Discord Community: Join our vibrant community of developers and enthusiasts to discuss ideas, ask questions, and share your projects.
  • Sample Applications: Explore a variety of sample applications to inspire your next project or learn new implementation techniques.
  • Explore Tutorials: Dive deeper into Twelve Labs capabilities with our comprehensive tutorials.

We encourage you to leverage these resources to expand your knowledge and create innovative applications using Twelve Labs' video understanding technology.

Generation Examples
No items found.
No items found.
Comparison against existing models
No items found.

Related articles

Building a Shade Finder App: Using Twelve Labs' API to Pinpoint Specific Colors in Videos

Whether you're looking to find the perfect berry-toned lipstick or just curious about spotting specific colors in your videos, this guide will help you leverage cutting-edge AI to do so effortlessly.

Meeran Kim
Building Advanced Video Understanding Applications: Integrating Twelve Labs Embed API with LanceDB for Multimodal AI

Leverage Twelve Labs Embed API and LanceDB to create AI applications that can process and analyze video content with unprecedented accuracy and efficiency.

James Le, Manish Maheshwari
A Recap of Denver Multimodal AI Hackathon

We had fun interacting with the AI community in Denver!

James Le
Advanced Video Search: Leveraging Twelve Labs and Milvus for Semantic Retrieval

Harness the power of Twelve Labs' advanced multimodal embeddings and Milvus' efficient vector database to create a robust video search solution.

James Le, Manish Maheshwari