Generating text description from youtube link using Angular, Gemini and Netlify
In this post we will implement an application to generate ai description from a youtube link using:
- angular with primeng for the ui
- gemini to generate the description
- netlify for ci/cd
Create app using angular cli
ng new angular-youtube-description
Add required dependencies
gemini
npm i @google/generative-ai
primeng
npm install primeng
- in angular.json add
...
"styles": [
"node_modules/primeng/resources/themes/lara-light-blue/theme.css",
"node_modules/primeng/resources/primeng.min.css",
...
]
and in styles.css add
@import "primeng/resources/themes/lara-light-blue/theme.css";
@import "primeng/resources/primeng.css";
Adding initial changes on app.component.ts
Let’s start adding some changes into app.component.ts
. Each property has a comment with an explanation
import { CommonModule } from '@angular/common';
import { Component, inject, OnInit, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { GoogleGenerativeAI } from '@google/generative-ai';
import { FormsModule } from '@angular/forms';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { take } from 'rxjs';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
@Component({
selector: 'app-root',
standalone: true,
imports: [
RouterOutlet,
CommonModule,
FormsModule,
HttpClientModule,
ButtonModule,
InputTextModule
],
templateUrl: './app.component.html',
styleUrl: './app.component.css',
})
export class AppComponent implements OnInit {
generatedDescription = signal<string | undefined>(undefined); // to see the description response in the template
videoUrl = signal<string | undefined>(undefined); // url we want to send to gemini
error = signal<string | undefined>(undefined); // to display an error in template when failing
isLoading = signal<boolean | undefined>(undefined); // to display in template when waiting for gemini response
#genAI: GoogleGenerativeAI | undefined; // to build a gemini model and generate content from prompt
#youtubeApiKey: string | undefined = ''; // add youtube key required to get video data
#geminiApiKey: string | undefined = ''; // add gemini key required to get gemini response
#http = inject(HttpClient); // to make a request to youtube video so we can get title and description to send to gemini
ngOnInit() {
this.#genAI = new GoogleGenerativeAI(this.#geminiApiKey!);
}
Generating required key for gemini
- go to Google AI Studio.
- login with your Google account.
- create an API key.
- once you get the key, assign it to
#geminiApiKey
inapp.component.ts
Generating required key for youtube
- follow this instructions https://developers.google.com/youtube/v3/getting-started
- once you get the key, assign it to
#youtubeApiKey
inapp.component.ts
Adding logic to get youtube video data and gemini response
getVideoDetails(videoUrl: string | undefined)
: we make a request to get youtube title and description so we can create gemini prompt. Since we need a videoId we added extractVideoId(url: string)
to extract it from url
generateDescription(title: string, originalDescription: string)
: in this method we create prompt to request gemini. We need to specify a model as parameter, in this case we use ‘gemini-pro’. Finally we execute generateContent
to assign the response to generatedDescription
signal so we can display it in the template
import { CommonModule } from '@angular/common';
import { Component, inject, OnInit, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { GoogleGenerativeAI } from '@google/generative-ai';
import { FormsModule } from '@angular/forms';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { take } from 'rxjs';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
@Component({
selector: 'app-root',
standalone: true,
imports: [
RouterOutlet,
CommonModule,
FormsModule,
HttpClientModule,
ButtonModule,
InputTextModule
],
templateUrl: './app.component.html',
styleUrl: './app.component.css',
})
export class AppComponent implements OnInit {
generatedDescription = signal<string | undefined>(undefined);
videoUrl = signal<string | undefined>(undefined);
error = signal<string | undefined>(undefined);
isLoading = signal<boolean | undefined>(undefined);
#genAI: GoogleGenerativeAI | undefined;
#youtubeApiKey: string | undefined = ''; // add youtube key required to get video data
#geminiApiKey: string | undefined = ''; // add gemini key required to get gemini response);
#http = inject(HttpClient);
ngOnInit() {
this.#genAI = new GoogleGenerativeAI(this.#geminiApiKey!);
}
getVideoDetails(videoUrl: string | undefined): void {
this.error.set(undefined);
const videoId = this.extractVideoId(videoUrl!);
if (!videoId) {
this.error.set('Invalid YouTube URL');
return;
}
const url = `https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=${videoId}&key=${
this.#youtubeApiKey
}`;
this.isLoading.set(true);
this.#http
.get(url)
.pipe(take(1))
.subscribe({
next: (response: any) => {
const videoData = response.items[0];
this.generateDescription(
videoData.snippet.title,
videoData.snippet.description
);
},
error: () => {
this.isLoading.set(false);
this.error.set('Unable to fetch video details');
},
});
}
private generateDescription(
title: string,
originalDescription: string
): void {
const prompt = {
input: `Generate a description for a YouTube video with the title: "${title}" and the following description: "${originalDescription}"`,
};
this.#genAI
?.getGenerativeModel({ model: 'gemini-pro' })
.generateContent(prompt.input)
.then((response: any) => {
this.generatedDescription.set(
response.response.candidates[0].content.parts[0]?.text
);
this.isLoading.set(false);
})
.catch(() => {
this.isLoading.set(false);
this.error.set('Unable to get response from Gemini AI model');
});
}
private extractVideoId(url: string): string | null {
const regex =
/(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/;
const match = url.match(regex);
return match ? match[1] : null;
}
}
Updating template on app.component.html
<div class="app-container">
<div class="app">
<h1>Enter youtube video url</h1>
<input
type="text"
pInputText
[(ngModel)]="videoUrl"
placeholder="Enter youtube video url"
/>
<p-button
type="submit"
[disabled]="isLoading()"
(click)="getVideoDetails(videoUrl())"
>
Get description
</p-button>
@if(isLoading()){
<p class="loading">Generating description from youtube video...</p>
} @else if(generatedDescription()){
<p>{{ generatedDescription() }}</p>
<p><i>Generated description by Gemini</i></p>
} @if(error()){
<p class="error">{{ error() }}</p>
}
</div>
</div>
Updating styles on app.component.css
.app-container {
justify-content: center;
align-items: center;
}
.app {
display: flex;
flex-direction: column;
align-items: center;
margin-inline: 20%;
}
p-button {
margin-top: 20px;
}
input {
width: 350px;
}
.error {
color: red;
}
.loading {
font-style: italic;
}
Deploying app using Netlify
We don’t want to expose our keys in our code, so we will use @ngx-env/builder
to add our keys in a .env
file to finally create environment variables on Netlify portal
1 - npm i @ngx-env/builder
and follow documentation
2 - update env.d.ts
declare interface Env {
readonly NODE_ENV: string;
// Replace the following with your own environment variables.
NG_APP_YOUTUBE_API_KEY: string;
NG_APP_GEMINI_API_KEY: string;
[key: string]: any;
}
2 - add your key to .env
file (IGNORE IT - DO NOT PUSH IT)
NG_APP_YOUTUBE_API_KEY=add your youtube api key here
NG_APP_GEMINI_API_KEY=add your gemini api key here
3 - update app.component.ts
to use env variables
#youtubeApiKey: string | undefined = import.meta.env.NG_APP_YOUTUBE_API_KEY;
#geminiApiKey: string | undefined = import.meta.env.NG_APP_GEMINI_API_KEY;
4 - deploy to Netlify following these steps
5 - add environment variables in Netlify Go to site configuration
6 - deploy your website
And that is it! Thanks for reading!
links:
- deployed website: https://unrivaled-choux-3b74f5.netlify.app/
- repo: https://github.com/salimchemes/angular-gemini
- gemini: https://gemini.google.com/
- @ngx-env/builder: https://www.npmjs.com/package/@ngx-env/builder
- primeng: https://primeng.org/