TranscriptIQ is a project that enables users to transcribe YouTube videos and perform various NLP (Natural Language Processing) tasks, chat with youtube video and many more on the transcribed text.
import streamlit as st
from io import BytesIO


result = BytesIO()

st.set_page_config(
                   page_title='TranscriptIQ',
                   page_icon='✨'
                )


st.header('TranscriptIQ')

st.markdown("""
TranscriptIQ allows you to talk ask questions any youtube video or video you upload. Under the hood it uses the latest AI technology to transcribe the audio and then search for the answer to your question in the transcript.
            
### Features 
* **Transcribe** any youtube video or video you upload
* **Search** for any question in the transcript
* **Highlight** the answer in the transcript
* **Chat** LLM powered AI to answer all your questions. 

You can check the source code at this [github repository](https://github.com/Devanshu-17/TranscriptIQ)

            
### How to use 
* Either upload a video or paste a youtube link
* Let the AI do the magic ask questions get answers, understand videos better. 
""")


st.markdown("""

----
### About the project
This project was developed as the part of LLM hackathon arranged by Streamlit. If any queries don't hesitate to reach out and raise an  issue in a repo :) .  
The project was created by [Devanshu](https://github.com/Devanshu-17), [Somesh](https://github.com/someshfengde).

""")

0_🌐_Home.py
Devanshu-17/TranscriptIQ
import os
import shutil
from datetime import datetime
from pytube import YouTube
from moviepy.editor import VideoFileClip
import whisper
import glob
import yt_dlp




def get_video_info(url):
    video = YouTube(url)

    video_info = {
    "title": video.title,
    "length": video.length,
    "thumbnail_url": video.thumbnail_url,
    "description": video.description,
    "views": video.views,
    "rating": video.rating,
    "age_restricted": video.age_restricted,
    "video_id": video.video_id,
    "author": video.author,
    "publish_date": video.publish_date,
    "captions": video.captions,
    "keywords": video.keywords,
    #"video_streams": video.streams,
    #"audio_streams": video.streams.filter(only_audio=True),
    #"video_tags": video.video_tags,
    #"length_seconds": video.length_seconds,
    #"average_rating": video.average_rating,
    #"is_closed_captioned": video.is_closed_captioned,
    "channel_url": video.channel_url,
    "channel_id": video.channel_id,
    #"channel_subscribers": video.channel_subscribers,
    #"related_videos": video.related_videos,
    #"captions_available": video.captions_available,
    #"thumbnail_resolution": video.thumbnail_resolution,
    #"captions_tracks": video.captions_tracks,

}


    return video_info

def current_directory(n):
    # Get the current directory
    current_directory = os.getcwd()
    # Get a list of all directories in the current directory
    directories = [name for name in os.listdir(current_directory) if os.path.isdir(os.path.join(current_directory, name))]
    # Filter directories that start with "My_Folder_"
    matching_directories = [name for name in directories if name.startswith("Folder_")]

    # Check if there are more than n matching directories
    if len(matching_directories) > n:
        # Remove the matching directories
        for directory in matching_directories:
            directory_path = os.path.join(current_directory, directory)
            shutil.rmtree(directory_path)
            print(f"Removed directory: {directory_path}")
    else:
        print("There are not enough matching directories to remove.")




def create_folder_and_directories():
    # Get the current date and time
    now = datetime.now()

    # Create a new folder with the current hour
    current_folder = f"Folder_{now.strftime('%Y%m%d_%H%M%S')}"
    os.makedirs(current_folder)

    # Create the subdirectories within the current folder
    mp4_directory = os.path.join(current_folder, 'media', 'mp4')
    mp3_directory = os.path.join(current_folder, 'media', 'mp3')
    txt_directory = os.path.join(current_folder, 'media', 'txt')

    # Create the subdirectories
    os.makedirs(mp4_directory)
    os.makedirs(mp3_directory)
    os.makedirs(txt_directory)
    return mp4_directory, mp3_directory, txt_directory



def download_youtube(video_url, mp4_directory):
    # Create a YouTube object
    yt = YouTube(video_url)
    # Get the lowest resolution video stream
    stream = yt.streams.get_lowest_resolution()
    # Download the video
    stream.download(mp4_directory)  
    return "mp4"



# https://gist.github.com/space-pope/977b0d15cf01932332014194fc80c1f0
def download_youtube1(video_url, download_path):

  opts = {
    "external_downloader": "ffmpeg",
    #"external_downloader_args": ffmpeg_args,
    # though not required, I'm including the subtitles options here for a reason; see below
    "writesubtitles": False,
    "writeautomaticsub": False,
    # to suppress ffmpeg's stdout output
    "quiet": True,
    "outtmpl": download_path + "/%(title)s.%(ext)s"
  }

  with yt_dlp.YoutubeDL(opts) as ydl:
    ydl.download(video_url)
    info = ydl.extract_info(video_url, download=False)
    duration = info.get("duration")
    return "webm",duration
  
def download_youtube2(video_url, download_path):
    opts = {
        "external_downloader": "ffmpeg",
        #"external_downloader_args": ffmpeg_args,
        # though not required, I'm including the subtitles options here for a reason; see below
        "writesubtitles": False,
        "writeautomaticsub": False,
        # to suppress ffmpeg's stdout output
        "quiet": True,
        "outtmpl": download_path + "/%(title)s.%(ext)s"
    }

    with yt_dlp.YoutubeDL(opts) as ydl:
        ydl.download(video_url)
        info = ydl.extract_info(video_url, download=False)
        duration = info.get("duration")
        return duration




def rename_videos(mp4_directory):
    # Get a list of all files in the directory
    files = os.listdir(mp4_directory)

    if len(files) > 1:
        # Iterate over each file and rename it
        for i, filename in enumerate(files, start=1):
            # Construct the current file path
            current_path = os.path.join(mp4_directory, filename)

            # Split the current filename and extension
            name, extension = os.path.splitext(filename)

            # Construct the new filename with numbering
            new_name = f"video_{i}{extension}"

            # Construct the new file path
            new_path = os.path.join(mp4_directory, new_name)

            # Rename the file
            os.rename(current_path, new_path)
    elif len(files) == 1:
        # Rename the single file as "video"
        filename = files[0]
        current_path = os.path.join(mp4_directory, filename)
        new_name = "video" + os.path.splitext(filename)[1]
        new_path = os.path.join(mp4_directory, new_name)
        os.rename(current_path, new_path)
    else:
        # No files in the directory
        print("No files found in the directory.")


def mp4_to_mp3(mp4_directory, video_extension, mp3_directory):
     # Load the video file
    video = VideoFileClip(f"{mp4_directory}/video.{video_extension}")

    # Extract the audio from the video file
    audio = video.audio

    # Save the audio as an MP3 file
    audio.write_audiofile(f"{mp3_directory}/my_audio.mp3", codec='mp3')

    # Close the video and audio files
    video.close()
    audio.close()




def transcribe_mp3(mp3_directory, name):
    model = whisper.load_model("tiny")
    result = model.transcribe(f"{mp3_directory}/{name}.mp3")
    return result


def with_opencv(filename):
    # video = cv2.VideoCapture(filename)
    # # count the number of frames
    # frames = video.get(cv2.CAP_PROP_FRAME_COUNT)
    # fps = video.get(cv2.CAP_PROP_FPS)
    
    # # calculate duration of the video
    # seconds = round(frames / fps)
    # video_time = timedelta(seconds=seconds).total_seconds()
      
    video = VideoFileClip(filename)
    video_time = video.duration
    return video_time






def concatenate_txt_files(directory):
    # Get a list of all text files in the directory
    txt_files = glob.glob(os.path.join(directory, "*.txt"))

    # Concatenate the contents of all text files
    concatenated_text = ""
    for txt_file in txt_files:
        with open(txt_file, "r") as file:
            text = file.read()
            concatenated_text += text

    return concatenated_text

myfunctions/my_functions.py
Devanshu-17/TranscriptIQ
from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2
import streamlit as st


def summarize_with_cohere(text):
    # Cohere credentials and model details
    PAT = st.secrets["PATS"]
    USER_ID = 'cohere'
    APP_ID = 'summarize'
    MODEL_ID = 'cohere-summarize'
    MODEL_VERSION_ID = 'bc1d5f9cc2834571b1322f572aca2305'


    channel = ClarifaiChannel.get_grpc_channel()
    stub = service_pb2_grpc.V2Stub(channel)

    metadata = (('authorization', 'Key ' + PAT),)

    # Create the input for Cohere summarization
    input_data = resources_pb2.Input(
        data=resources_pb2.Data(
            text=resources_pb2.Text(
                raw=text
            )
        )
    )

    post_model_outputs_response = stub.PostModelOutputs(
        service_pb2.PostModelOutputsRequest(
            user_app_id=resources_pb2.UserAppIDSet(user_id=USER_ID, app_id=APP_ID),
            model_id=MODEL_ID,
            version_id=MODEL_VERSION_ID,
            inputs=[input_data]
        ),
        metadata=metadata
    )

    if post_model_outputs_response.status.code != status_code_pb2.SUCCESS:
        raise Exception(f"Post model outputs failed, status: {post_model_outputs_response.status.description}")

    # Extract the summary from Cohere response
    summary = post_model_outputs_response.outputs[0].data.text.raw
    return summary

import spacy
from pyecharts import options as opts
from pyecharts.charts import Graph, Page
from pyecharts.globals import ThemeType
import emoji

def ner_spacy(text):
    nlp = spacy.load("en_core_web_sm")
    doc = nlp(text)
    return doc.ents

def filter_special_chars(text):
    return emoji.demojize(text)

def get_graph(transcription_text, save_path, DEBUG = False):
    """Generates the graph with pyecharts and save that grapn in save_path

    Parameters
    ----------
    transcription_text : str
        Transcription text from youtube for doing NER. 
    save_path : str (Path that should end with HTML) 
        save path should refer to the file which has to be saved to (.html) format file
    """
    DEBUG = True 
    data = {}
    transcription_text = filter_special_chars(transcription_text)
    for ent in ner_spacy(transcription_text):
        if ent.label_ not in data.keys():
            data[ent.label_] = [ent.text]
        else:
            data[ent.label_].append(ent.text)



    category_list = list(data.keys())
    # prepare categories
    categories = [
        opts.GraphCategory(name=n)
        for i, n in enumerate(category_list)
    ]

    # prepare nodes
    nodes = []
    for key, values in data.items():
        values = list(set(values))  # Removing duplicates by converting the list to set and then back to a list.
        
        for value in values[:10]:
            nodes.append(
                opts.GraphNode(
                    name=f"{value}", 
                    symbol_size=10,
                    value=value, 
                    category=key,
                    label_opts=opts.LabelOpts(is_show=True)
                )
            )

    # Add key nodes
    for key in data.keys():
        nodes.append(
            opts.GraphNode(
                name=key, 
                symbol_size=20, 
                category=key, 
                label_opts=opts.LabelOpts(is_show=True)
            )
        )

    # prepare links
    links = []
    for key, values in data.items():
        values = list(set(values))  # Removing duplicates by converting the list to set and then back to a list.
        
        for value in values:
            links.append(opts.GraphLink(source=key, target=f"{value}"))
    if DEBUG : 
        print('=' * 15 + 'links' + '=' * 15) 
        print(links)
        print('='* 30)
    # Add links between key nodes
    prev_key = None
    for key in data.keys():
        if prev_key:
            links.append(opts.GraphLink(source=prev_key, target=key))
        prev_key = key

    (
        Graph(init_opts=opts.InitOpts(theme=ThemeType.DARK))
        .add(
            "",
            nodes=nodes,
            links=links,
            categories=categories,
            layout="force",
            repulsion= 800, 
            is_rotate_label=True,
            linestyle_opts=opts.LineStyleOpts(color="source", curve=0.3),
            label_opts=opts.LabelOpts(position="right"),
        )
        .set_global_opts(title_opts=opts.TitleOpts(title=""))
        .render(save_path)
    )
myfunctions/my_summarization_functions.py
Devanshu-17/TranscriptIQ
import streamlit as st
import os
from datetime import datetime
from moviepy.editor import VideoFileClip
from myfunctions.my_functions import current_directory, create_folder_and_directories, download_youtube, rename_videos, mp4_to_mp3, transcribe_mp3, download_youtube1, get_video_info
from myfunctions.my_summarization_functions import summarize_with_cohere
from myfunctions.my_summarization_functions import ner_spacy, get_graph
from io import BytesIO
from annotated_text import annotated_text
import pandas as pd
import plotly.express as px
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
import pyecharts.options as opts
from pyecharts.charts import Bar3D
from collections import Counter
from pyecharts.charts import WordCloud
from pyecharts.charts import Gauge
from dataclasses import dataclass
from typing import Literal
import streamlit as st

from langchain import OpenAI
from langchain.callbacks import get_openai_callback
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationSummaryMemory
import streamlit.components.v1 as components
result = BytesIO()

st.set_page_config(layout="wide",
                   page_title='Transcribe YouTube Video',
                   page_icon=':video_camera:')

# make any grid with a function
def make_grid(cols,rows):
    grid = [0]*cols
    for i in range(cols):
        with st.container():
            grid[i] = st.columns(rows)
    return grid

# with st.sidebar:
#     st.image('images/Robot_whisper2.jpg')

st.header('Transcribe YouTube Video :video_camera:')



current_directory(3)



#if youtube_button:

try:
  # check if the key exists in session state
  _ = st.session_state.keep_graphics
except AttributeError:
  # otherwise set it to false
  st.session_state.keep_graphics = False


# Specify the YouTube video URL
video_url = st.text_input('Youtube link', 'https://www.youtube.com/watch?v=iwNO4nbmkVs')
youtube_button = st.button('Transcribe')

if youtube_button or st.session_state.keep_graphics:
    info = get_video_info(video_url)
    st.markdown(f"#### {info['title']}")

    grid_video = make_grid(1,6)

    with grid_video[0][0]:
         st.markdown(f"""
                     Views
                     #### {info['views']}
                     """)
    with grid_video[0][1]:
         st.markdown(f"""
                     Length
                     #### {info['length']} sec.
                     """)
    with grid_video[0][2]:
        st.markdown(f"""
                    Author
                    #### {info['author']}
                    """)
    with grid_video[0][3]:
        st.markdown(f"""
                    video id
                    #### {info['video_id']}
                    """)
    with grid_video[0][4]:
        
        st.markdown(f"""
                    Publish date
                    #### {datetime.strftime(info['publish_date'], "%Y-%m-%d")}
                    """)
    with grid_video[0][5]:
        st.image(info['thumbnail_url'])


    if info['age_restricted']:
        st.warning('This video is age restricted. The application will stop at this point.', icon="⚠️")
    else :
        mp4_directory, mp3_directory, txt_directory = create_folder_and_directories()
        duration = info['length']

        with st.spinner("Download Youtube as MP4"):

            try:
                video_extension = download_youtube(video_url, mp4_directory)

            except:
                with st.spinner("Please wait, the application is currently unable to download the video in MP4 format. It is currently attempting to download the video in WebM format instead. This process may take some time. Thank you for your patience."):
                    video_extension,duration= download_youtube1(video_url, mp4_directory)
                    # duration= download_youtube1(video_url, mp4_directory)


            rename_videos(mp4_directory)
            video_mp4 = os.path.join(mp4_directory, f"video.{video_extension}")
            # video_mp4 = os.path.join(mp4_directory, "video.webm")

        # Check the duration of the video in seconds
        if duration < 180 :# 3 minutes
                
                with st.spinner("Convert MP4 to MP3"):
            
                    
                    mp4_to_mp3(mp4_directory, video_extension, mp3_directory )
                    

                    
                
                col1, col2 = st.columns(2)   

                        

                
                col2.audio(f"{mp3_directory}/my_audio.mp3")
                
                with st.spinner("Transcribe YouTube Video ... "):
                        
                        result = transcribe_mp3(mp3_directory, "my_audio") 
        
                        txt_path = f"{txt_directory}/output.txt" 
        
                        # Open the file in write mode
                        with open(txt_path, 'w') as file:
                            # Write the data to the file
                            file.write(result['text'])

                WebVTT = "WEBVTT"
                with col1:
                    st.info(f"Detected language: {result['language']}")


                    
                    for segment in result['segments']:
                        start = round(float(segment['start']),2)
                        end = round(float(segment['end']),2)
                        text = segment['text']
                        WebVTT += f""" \n[{start} : {end}] : {text}"""
                    

                    user_text = st.text_area("The Complete Text",result['text'], height=450)
                
                with st.expander("WebVTT"):
                        st.text_area("Format Web Video Text Tracks (WebVTT)", WebVTT, height=200)

                    

                col2.video(video_url)
                                                                                
                #st.download_button('Download text as csv', result['text'])
                st.download_button(
                        label=f"Download as txt",
                        data=result['text'],
                        file_name=f'Transcribe YouTube Video {datetime.now()}.txt',
                        mime='text/plain'
                    )
        

        else:
                
                st.warning(f'The duration of the video exceeds 3 minutes ({round(duration/60,2)} minutes). To handle this, the application will divide the video into multiple 3-minute segments and convert each segment into an MP3 file. ', icon="⚠️")
                col1, col2 = st.columns(2)

                        
                video = VideoFileClip(video_mp4)
                #duration = video.duration

                # Calculate the number of 3-minute segments
                num_segments = int(duration // 180) + 1
                result_text = ''
                WebVTT = "WEBVTT"
                for i in range(num_segments):
                    
                        # Set the start and end times for the segment
                        start_time = i * 180  # 3 minutes
                        end_time = min((i + 1) * 180, duration)  # 3 minutes or remainder of the video
                        with st.spinner(f"Please wait ..."):
                            # Extract the segment and convert to MP3
                            segment = video.subclip(start_time, end_time)
                            output_filename = f"{mp3_directory}/segment_{i + 1}.mp3"
                            segment.audio.write_audiofile(output_filename)

                            col2.audio(f"{mp3_directory}/segment_{i + 1}.mp3")


                        with st.spinner(f"Transcribe from : {round(start_time/60,2)} min. to {round(end_time/60,2)} min."):
                            result = transcribe_mp3(mp3_directory, f"segment_{i + 1}")
                        col1.write(f"Transcribe from : {round(start_time/60,2)} min. to {round(end_time/60,2)} min. : OK")
                        for segment in result['segments']:
                            start = round(float(segment['start']),2)
                            end = round(float(segment['end']),2)
                            text = segment['text']
                            #col1.markdown(f"""[{start} : {end}] : {text}""")
                            WebVTT += f""" \n[{start} : {end}] : {text}"""


                        result_text = result_text + result['text'] + " "

                with col1:
                    st.info(f"Detected language: {result['language']}")
                    
                

                video.close()
                col2.video(video_url)


                #concatenated_text = concatenate_txt_files(txt_directory)
                #st.download_button('Download text as csv', concatenated_text)
                result = result_text

                txt_path = f"{txt_directory}/output.txt" 
        
                # Open the file in write mode
                with open(txt_path, 'w') as file:
                    # Write the data to the file
                    file.write(result)

                user_text = col1.text_area("The Complete Text",result, height=450)
                with st.expander("WebVTT"):
                        st.text_area("Format Web Video Text Tracks (WebVTT)", WebVTT, height=200)
        
                col1.download_button(
                            label=f"Download as txt",
                            data=result,
                            file_name=f'Transcribe YouTube Video {datetime.now()}.txt',
                            mime='text/plain'
                        )


        txt_path = f"{txt_directory}/output.txt" 
        file = open(txt_path, "r")
        # Read the contents of the file
        file_contents = file.read()

        # Close the file
        file.close()
        
        # st.text_area("The Summarized Text",sample_extractive_summarization([file_contents]), height=400)
        # #st.text_area("The Summarized Text",file_contents, height=400)
        # st.write()

        st.markdown("### Text summarization ")



        
        with st.spinner("Summarization ..."):
                extractive_summarization = summarize_with_cohere(file_contents)
        st.text_area("Summary: ", extractive_summarization)
        
        st.markdown("---")
    

        st.markdown("""### 🇳 🇪 🇷 (Named Entity Recognition)""")
        st.write("Named Entity Recognition (NER) is a natural language processing (NLP) task that involves identifying and classifying named entities within text into predefined categories. These entities can include names of people, organizations, locations, dates, quantities, and more as you can see in the photo.")
        st.image("images/NER.png")
        
        # Change it to use your custom NER function
      
        with st.spinner("NER (Named Entity Recognition) ..."):
                ner_entities = ner_spacy(file_contents)
                annotated_text(*[(ent.text, ent.label_) for ent in ner_entities])

        # Update this section to use the custom NER results and generate the graph
        
        with st.spinner("Generating and displaying the graph..."):
                save_path = 'output.html'  # Set the path where you want to save the graph
                get_graph(file_contents, save_path)
                st.subheader("Graph - NER graph")
                st.components.v1.html(open(save_path, 'r').read(), height=600, width=900)

        # Perform Named Entity Recognition (NER) and extract entities
        with st.spinner("Generating and displaying the word cloud..."):
            ner_entities = ner_spacy(file_contents)

        # Extract named entities and their counts
        ner_entity_counts = Counter([ent.text for ent in ner_entities])

        # Create a list of tuples containing (entity, count) for word cloud
        words = [(entity, count) for entity, count in ner_entity_counts.items()]

       # Create the word cloud
        wordcloud = (
            WordCloud()
            .add(
                "",
                words,
                word_size_range=[20, 100],
                textstyle_opts=opts.TextStyleOpts(font_family="cursive"),
            )
            .set_global_opts(title_opts=opts.TitleOpts(title=""))
        )

        # Save the word cloud as an HTML file
        wordcloud.render("wordcloud_ner.html")
        st.session_state.keep_graphics = True
        # Display the word cloud using Streamlit
        st.subheader("Word Cloud")
        with open("wordcloud_ner.html", "r") as f:
            html = f.read()
        st.components.v1.html(html, height=600, width=900)



        st.markdown("---")
        st.markdown("""### Sentiment Analysis 😃 😶 😡 """)
        st.write("""
        The `compound score` typically ranges from -1 to +1, where -1 indicates extremely negative sentiment, +1 indicates extremely positive sentiment, and 0 represents neutral sentiment.
            """)

        analyser = SentimentIntensityAnalyzer()
        score = analyser.polarity_scores(file_contents)
        polarity_vader = score['compound']

        mygrid2 = make_grid(1,3)
        with mygrid2[0][1]:
            st.markdown(f"##### Compound Score = {polarity_vader}")

        if polarity_vader > 0:
            st.write("The sentiment of this text is likely to be positive")
        elif polarity_vader == 0 :
            st.write("The sentiment of this text is likely to be neutral")
        else:
            st.write("The sentiment of this text is likely to be negative")
            
        # fig = plot_sentiment(polarity_vader)
        # # Display the plot in Streamlit
        # st.pyplot(fig)

# Create the gauge chart with different color segments
        gauge_chart = (
            Gauge()
            .add(
                "Sentiment",
                [("Sentiment", polarity_vader * 100)],
                axisline_opts=opts.AxisLineOpts(
                    linestyle_opts=opts.LineStyleOpts(
                        color=[(0.3, "#67e0e3"), (0.7, "#37a2da"), (1, "#fd666d")], width=30
                    )
                ),
            )
            .set_global_opts(
                title_opts=opts.TitleOpts(title="Sentiment Gauge"),
                legend_opts=opts.LegendOpts(is_show=False),
            )
        )

        # Save the gauge chart as an HTML file
        gauge_chart.render("gauge_chart_color.html")

        # Read the HTML file and display its content using Streamlit
        with open("gauge_chart_color.html", "r") as f:
            gauge_html_color = f.read()
        st.components.v1.html(gauge_html_color, height=500, width=800)


        @dataclass
        class Message:
            """Class for keeping track of a chat message."""
            origin: Literal["human", "ai"]
            message: str

        def load_css():
            with open("pages/static/styles.css", "r") as f:
                css = f"<style>{f.read()}</style>"
                st.markdown(css, unsafe_allow_html=True)

        def initialize_session_state():
            if "history" not in st.session_state:
                st.session_state.history = []
            if "token_count" not in st.session_state:
                st.session_state.token_count = 0
            if "conversation" not in st.session_state:
                llm = OpenAI(
                    temperature=0,
                    openai_api_key=st.secrets["openai_api_key"],
                    model_name="text-davinci-003"
                )
                st.session_state.conversation = ConversationChain(
                    llm=llm,
                    memory=ConversationSummaryMemory(llm=llm),
                )

        def on_click_callback():
            with get_openai_callback() as cb:
                context_summary = extractive_summarization
                human_prompt_before = st.session_state.human_prompt
                human_prompt = f"""{st.session_state.human_prompt} 
                ---
                context for summarisation is
                {context_summary}
                """
                llm_response = st.session_state.conversation.run(
                    human_prompt
                )
                st.session_state.history.append(
                    Message("human", human_prompt_before)
                )
                st.session_state.history.append(
                    Message("ai", llm_response)
                )
                st.session_state.token_count += cb.total_tokens

        load_css()
        initialize_session_state()

        st.title("TranscriptAI: Talk with video 🧙‍♂️ ")

        chat_placeholder = st.container()
        prompt_placeholder = st.form("chat-form")
        credit_card_placeholder = st.empty()

        with chat_placeholder:
            for chat in st.session_state.history:
                div = f"""
        <div class="chat-row 
            {'' if chat.origin == 'ai' else 'row-reverse'}">
            <img class="chat-icon" src="{
                'https://raw.githubusercontent.com/Devanshu-17/TranscriptIQ/main/pages/static/ai_icon.png' if chat.origin == 'ai' 
                            else 'https://raw.githubusercontent.com/Devanshu-17/TranscriptIQ/main/pages/static/user_icon.png'}"
                width=32 height=32>
            <div class="chat-bubble
            {'ai-bubble' if chat.origin == 'ai' else 'human-bubble'}">
                &#8203;{chat.message}
            </div>
        </div>
                """
                st.markdown(div, unsafe_allow_html=True)
            
            for _ in range(3):
                st.markdown("")

        with prompt_placeholder:
            st.markdown("**Chat**")
            cols = st.columns((6, 1))
            cols[0].text_input(
                "Chat",
                value="Hey",
                label_visibility="collapsed",
                key="human_prompt",
            )
            cols[1].form_submit_button(
                "Send", 
                type="primary", 
                on_click=on_click_callback, 
            )

        components.html("""
        <script>
        const streamlitDoc = window.parent.document;

        const buttons = Array.from(
            streamlitDoc.querySelectorAll('.stButton > button')
        );
        const submitButton = buttons.find(
            el => el.innerText === 'Submit'
        );

        streamlitDoc.addEventListener('keydown', function(e) {
            switch (e.key) {
                case 'Enter':
                    submitButton.click();
                    break;
            }
        });
        </script>
        """, 
            height=0,
            width=0,
        )

        for i in range(20):
            st.write("")



        st.markdown("""
        ----
        **Contact** :

        Made with :heart: for Streamlit LLM Hackathon
        """)

pages/1_🎧_Transcribe Youtube.py
Devanshu-17/TranscriptIQ
from dataclasses import dataclass
from typing import Literal
import streamlit as st

from langchain import OpenAI
from langchain.callbacks import get_openai_callback
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationSummaryMemory
import streamlit.components.v1 as components

@dataclass
class Message:
    """Class for keeping track of a chat message."""
    origin: Literal["human", "ai"]
    message: str

def load_css():
    with open("pages/static/styles.css", "r") as f:
        css = f"<style>{f.read()}</style>"
        st.markdown(css, unsafe_allow_html=True)

def initialize_session_state():
    if "history" not in st.session_state:
        st.session_state.history = []
    if "token_count" not in st.session_state:
        st.session_state.token_count = 0
    if "conversation" not in st.session_state:
        llm = OpenAI(
            temperature=0,
            openai_api_key=st.secrets["openai_api_key"],
            model_name="text-davinci-003"
        )
        st.session_state.conversation = ConversationChain(
            llm=llm,
            memory=ConversationSummaryMemory(llm=llm),
        )

def on_click_callback():
    with get_openai_callback() as cb:
        human_prompt = st.session_state.human_prompt
        llm_response = st.session_state.conversation.run(
            human_prompt
        )
        st.session_state.history.append(
            Message("human", human_prompt)
        )
        st.session_state.history.append(
            Message("ai", llm_response)
        )
        st.session_state.token_count += cb.total_tokens

load_css()
initialize_session_state()

st.title("TranscriptAI 🧙‍♂️ ")

chat_placeholder = st.container()
prompt_placeholder = st.form("chat-form")
credit_card_placeholder = st.empty()

with chat_placeholder:
    for chat in st.session_state.history:
        div = f"""
<div class="chat-row 
    {'' if chat.origin == 'ai' else 'row-reverse'}">
    <img class="chat-icon" src="{
        'https://raw.githubusercontent.com/Devanshu-17/TranscriptIQ/main/pages/static/ai_icon.png' if chat.origin == 'ai' 
                    else 'https://raw.githubusercontent.com/Devanshu-17/TranscriptIQ/main/pages/static/user_icon.png'}"
        width=32 height=32>
    <div class="chat-bubble
    {'ai-bubble' if chat.origin == 'ai' else 'human-bubble'}">
        &#8203;{chat.message}
    </div>
</div>
        """
        st.markdown(div, unsafe_allow_html=True)
    
    for _ in range(3):
        st.markdown("")

with prompt_placeholder:
    st.markdown("**Chat**")
    cols = st.columns((6, 1))
    cols[0].text_input(
        "Chat",
        value="Hey",
        label_visibility="collapsed",
        key="human_prompt",
    )
    cols[1].form_submit_button(
        "Send", 
        type="primary", 
        on_click=on_click_callback, 
    )

components.html("""
<script>
const streamlitDoc = window.parent.document;

const buttons = Array.from(
    streamlitDoc.querySelectorAll('.stButton > button')
);
const submitButton = buttons.find(
    el => el.innerText === 'Submit'
);

streamlitDoc.addEventListener('keydown', function(e) {
    switch (e.key) {
        case 'Enter':
            submitButton.click();
            break;
    }
});
</script>
""", 
    height=0,
    width=0,
)
pages/2_💬_Chat.py
Devanshu-17/TranscriptIQ