File size: 11,230 Bytes
3299970
16034fb
 
 
a9bc837
da14581
16034fb
a9bc837
16034fb
3299970
 
16034fb
3299970
 
16034fb
3299970
 
16034fb
3299970
 
16034fb
3299970
 
16034fb
3299970
 
16034fb
a9bc837
16034fb
a9bc837
 
 
 
 
 
16034fb
 
 
 
 
3299970
 
 
 
 
 
 
 
 
 
16034fb
 
3299970
 
16034fb
 
 
 
 
 
 
 
a9bc837
3299970
16034fb
a9bc837
16034fb
a9bc837
2937856
a9bc837
16034fb
 
 
 
a9bc837
 
 
 
 
 
16034fb
a9bc837
16034fb
3299970
16034fb
3299970
 
cad98a9
3299970
 
374286c
a9bc837
 
 
 
 
 
 
 
16034fb
 
3299970
 
 
 
 
16034fb
 
 
 
3299970
 
 
 
 
 
 
 
 
16034fb
 
a9bc837
16034fb
 
 
 
 
 
 
a9bc837
2937856
a9bc837
16034fb
 
 
 
a9bc837
 
 
 
 
 
16034fb
a9bc837
16034fb
3299970
16034fb
3299970
 
2937856
a9bc837
 
374286c
a9bc837
 
 
 
 
 
 
 
16034fb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a9bc837
 
 
 
 
 
 
 
 
16034fb
 
 
a9bc837
 
 
 
 
16034fb
a9bc837
 
 
 
 
 
16034fb
374286c
16034fb
a9bc837
3299970
a9bc837
 
 
 
 
 
 
134aa14
 
16034fb
 
 
 
a9bc837
 
 
 
 
 
16034fb
 
a9bc837
 
 
 
 
16034fb
a9bc837
 
 
 
 
 
16034fb
3299970
16034fb
3299970
a9bc837
 
3299970
a9bc837
 
 
 
 
 
16034fb
 
1a921fb
dc20cd0
a9bc837
16034fb
a9bc837
16034fb
a9bc837
 
 
 
16034fb
a9bc837
2937856
a9bc837
16034fb
a9bc837
 
16034fb
 
 
 
 
a9bc837
 
16034fb
a9bc837
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# Imports
import gradio as gr
import whisper
from pytube import YouTube
from transformers import pipeline, T5Tokenizer, T5ForConditionalGeneration
from wordcloud import WordCloud

class GradioInference:
    def __init__(self):
        
        # OpenAI's Whisper model sizes
        self.sizes = list(whisper._MODELS.keys())

        # Whisper's available languages for ASR
        self.langs = ["none"] + sorted(list(whisper.tokenizer.LANGUAGES.values()))
        
        # Default size
        self.current_size = "base"
        
        # Default model size
        self.loaded_model = whisper.load_model(self.current_size)
        
        # Initialize Pytube Object
        self.yt = None

        # Initialize summary model
        self.summarizer = pipeline("summarization", model="facebook/bart-large-cnn")

        # Initialize VoiceLabT5 model and tokenizer
        self.keyword_model = T5ForConditionalGeneration.from_pretrained(
            "Voicelab/vlt5-base-keywords"
        )
        self.keyword_tokenizer = T5Tokenizer.from_pretrained(
            "Voicelab/vlt5-base-keywords"
        )

        # Sentiment Classifier
        self.classifier = pipeline("text-classification")

    def __call__(self, link, lang, size):
        """
        Call the Gradio Inference python class.
        This class gets access to a YouTube video using python's library Pytube and downloads its audio.
        Then it uses the Whisper model to perform Automatic Speech Recognition (i.e Speech-to-Text).
        Once the function has the transcription of the video it proccess it to obtain:
            - Summary: using Facebook's BART transformer.
            - KeyWords: using VoiceLabT5 keyword extractor.
            - Sentiment Analysis: using Hugging Face's default sentiment classifier
            - WordCloud: using the wordcloud python library.
        """
        if self.yt is None:
            self.yt = YouTube(link)
        
        # Pytube library to access to YouTube audio stream
        path = self.yt.streams.filter(only_audio=True)[0].download(filename="tmp.mp4")

        if lang == "none":
            lang = None

        if size != self.current_size:
            self.loaded_model = whisper.load_model(size)
            self.current_size = size

        # Transcribe the audio extracted from pytube
        results = self.loaded_model.transcribe(path, language=lang)

        # Perform summarization on the transcription
        transcription_summary = self.summarizer(
            results["text"], max_length=512, min_length=30, do_sample=False
        )

        # Extract keywords using VoiceLabT5
        task_prefix = "Keywords: "
        input_sequence = task_prefix + results["text"]
        input_ids = self.keyword_tokenizer(
            input_sequence, return_tensors="pt", truncation=False
        ).input_ids
        output = self.keyword_model.generate(
            input_ids, no_repeat_ngram_size=3, num_beams=4
        )
        predicted = self.keyword_tokenizer.decode(output[0], skip_special_tokens=True)
        keywords = [x.strip() for x in predicted.split(",") if x.strip()]

        # Sentiment label
        label = self.classifier(results["text"])[0]["label"]
        
        # Generate WordCloud object
        wordcloud = WordCloud().generate(results["text"])

        # WordCloud image to display
        wordcloud_image = wordcloud.to_image()

        return (
            results["text"],
            transcription_summary[0]["summary_text"],
            keywords,
            label,
            wordcloud_image,
        )

    def populate_metadata(self, link):
        """
        Access to the YouTube video title and thumbnail image to further display it
        params:
        - link: a YouTube URL.
        """
        self.yt = YouTube(link)
        return self.yt.thumbnail_url, self.yt.title

    def from_audio_input(self, lang, size, audio_file):
        """
        Call the Gradio Inference python class.
        Uses it directly the Whisper model to perform Automatic Speech Recognition (i.e Speech-to-Text).
        Once the function has the transcription of the video it proccess it to obtain:
            - Summary: using Facebook's BART transformer.
            - KeyWords: using VoiceLabT5 keyword extractor.
            - Sentiment Analysis: using Hugging Face's default sentiment classifier
            - WordCloud: using the wordcloud python library.
        """
        if lang == "none":
            lang = None

        if size != self.current_size:
            self.loaded_model = whisper.load_model(size)
            self.current_size = size

        results = self.loaded_model.transcribe(audio_file, language=lang)

        # Perform summarization on the transcription
        transcription_summary = self.summarizer(
            results["text"], max_length=512, min_length=30, do_sample=False
        )

        # Extract keywords using VoiceLabT5
        task_prefix = "Keywords: "
        input_sequence = task_prefix + results["text"]
        input_ids = self.keyword_tokenizer(
            input_sequence, return_tensors="pt", truncation=False
        ).input_ids
        output = self.keyword_model.generate(
            input_ids, no_repeat_ngram_size=3, num_beams=4
        )
        predicted = self.keyword_tokenizer.decode(output[0], skip_special_tokens=True)
        keywords = [x.strip() for x in predicted.split(",") if x.strip()]

        # Sentiment label    
        label = self.classifier(results["text"])[0]["label"]
        
        # WordCloud object
        wordcloud = WordCloud().generate(
            results["text"]
        )
        wordcloud_image = wordcloud.to_image()

        return (
            results["text"],
            transcription_summary[0]["summary_text"],
            keywords,
            label,
            wordcloud_image,
        )


gio = GradioInference()
title = "Youtube Insights"
description = "Your AI-powered video analytics tool"

block = gr.Blocks()
with block as demo:
    gr.HTML(
        """
        <div style="text-align: center; max-width: 500px; margin: 0 auto;">
          <div>
            <h1>Youtube <span style="color: red;">Insights</span> 📹</h1>
          </div>
          <p style="margin-bottom: 10px; font-size: 94%">
            Your AI-powered video analytics tool
          </p>
        </div>
        """
    )
    with gr.Group():
        with gr.Tab("From YouTube"):
            with gr.Box():
                with gr.Row().style(equal_height=True):
                    size = gr.Dropdown(
                        label="Model Size", choices=gio.sizes, value="base"
                    )
                    lang = gr.Dropdown(
                        label="Language (Optional)", choices=gio.langs, value="none"
                    )
                link = gr.Textbox(
                    label="YouTube Link", placeholder="Enter YouTube link..."
                )
                title = gr.Label(label="Video Title")
                with gr.Row().style(equal_height=True):
                    img = gr.Image(label="Thumbnail")
                    text = gr.Textbox(
                        label="Transcription",
                        placeholder="Transcription Output...",
                        lines=10,
                    ).style(show_copy_button=True, container=True)
                with gr.Row().style(equal_height=True):
                    summary = gr.Textbox(
                        label="Summary", placeholder="Summary Output...", lines=5
                    ).style(show_copy_button=True, container=True)
                    keywords = gr.Textbox(
                        label="Keywords", placeholder="Keywords Output...", lines=5
                    ).style(show_copy_button=True, container=True)
                    label = gr.Label(label="Sentiment Analysis")
                    wordcloud_image = gr.Image()
                with gr.Row().style(equal_height=True):
                    clear = gr.ClearButton(
                        [link, title, img, text, summary, keywords, label, wordcloud_image], scale=1
                    )
                    btn = gr.Button("Get video insights", variant="primary", scale=1)
                btn.click(
                    gio,
                    inputs=[link, lang, size],
                    outputs=[text, summary, keywords, label, wordcloud_image],
                )
                if link:
                    link.change(gio.populate_metadata, inputs=[link], outputs=[img, title])

        with gr.Tab("From Audio file"):
            with gr.Box():
                with gr.Row().style(equal_height=True):
                    size = gr.Dropdown(
                        label="Model Size", choices=gio.sizes, value="base"
                    )
                    lang = gr.Dropdown(
                        label="Language (Optional)", choices=gio.langs, value="none"
                    )
                audio_file = gr.Audio(type="filepath")
                with gr.Row().style(equal_height=True):
                    text = gr.Textbox(
                        label="Transcription",
                        placeholder="Transcription Output...",
                        lines=10,
                    ).style(show_copy_button=True, container=False)
                with gr.Row().style(equal_height=True):
                    summary = gr.Textbox(
                        label="Summary", placeholder="Summary Output", lines=5
                    )
                    keywords = gr.Textbox(
                        label="Keywords", placeholder="Keywords Output", lines=5
                    )
                    label = gr.Label(label="Sentiment Analysis")
                    wordcloud_image = gr.Image()
                with gr.Row().style(equal_height=True):
                    clear = gr.ClearButton([audio_file,text, summary, keywords, label, wordcloud_image], scale=1)
                    btn = gr.Button(
                        "Get video insights", variant="primary", scale=1
                    )
                btn.click(
                    gio.from_audio_input,
                    inputs=[lang, size, audio_file],
                    outputs=[text, summary, keywords, label, wordcloud_image],
                )


with block:
    gr.Markdown("### Video Examples")
    gr.Examples(["https://www.youtube.com/shorts/xDNzz8yAH7I"], inputs=link)

    gr.Markdown("About the app:")

    with gr.Accordion("What is YouTube Insights?", open=False):
        gr.Markdown(
            "YouTube Insights is a tool developed with academic purposes only, that creates summaries, keywords and sentiments analysis based on YouTube videos or user audio files."
        )

    with gr.Accordion("How does it work?", open=False):
        gr.Markdown(
            "Works by using OpenAI's Whisper, BART for summarization and VoiceLabT5 for Keyword Extraction."
        )

    gr.HTML(
        """
        <div style="text-align: center; max-width: 500px; margin: 0 auto;">
          <p style="margin-bottom: 10px; font-size: 96%">
            2023 Master in Big Data & Data Science - Universidad Complutense de Madrid
          </p>
        </div>
        """
    )

demo.launch()