Logo

dev-resources.site

for different kinds of informations.

Generating text description from youtube link using Angular, Gemini and Netlify

Published at
9/25/2024
Categories
angular
gemini
ai
netlify
Author
salimchemes
Categories
4 categories in total
angular
open
gemini
open
ai
open
netlify
open
Author
11 person written this
salimchemes
open
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",
    ...
]
Enter fullscreen mode Exit fullscreen mode

and in styles.css add

@import "primeng/resources/themes/lara-light-blue/theme.css";
@import "primeng/resources/primeng.css";
Enter fullscreen mode Exit fullscreen mode

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!);
  }
Enter fullscreen mode Exit fullscreen mode

Generating required key for gemini

  1. go to Google AI Studio.
  2. login with your Google account.
  3. create an API key.
  4. once you get the key, assign it to #geminiApiKey in app.component.ts

Generating required key for youtube

  1. follow this instructions https://developers.google.com/youtube/v3/getting-started
  2. once you get the key, assign it to #youtubeApiKey in app.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;
  }
}

Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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;
}

Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

4 - deploy to Netlify following these steps

5 - add environment variables in Netlify
Go to site configuration

Image description

Image description

6 - deploy your website

And that is it! Thanks for reading!

links:

netlify Article's
30 articles in total
Favicon
Netlify
Favicon
Netlify + FalkorDB: GRAPH Database Integration for Netlify Just Got Easier
Favicon
Seller Center
Favicon
https://papaya-starburst-602e97.netlify.app/#tools
Favicon
Question: How do I host my angular front-end on Netlify
Favicon
Building Social Media Automation: LinkedIn Sharing with Serverless Function
Favicon
Internet Speed Test
Favicon
Radio worlwide station[20]
Favicon
How to deploy a Node.js Express app on Netlify (2024)
Favicon
Boost Developer Productivity With LambdaTest and Netlify Integration
Favicon
Building a Custom Shipping Calculator with Stripe and Netlify Functions for Multi-Currency (โ‚ฌ/$), Quantity, and Location Support
Favicon
Cross-Posting and Portfolio Project Update: Optimizing API Calls and Implementing Best Practices
Favicon
Unexpected Adventure with Leonardo AI: A Developerโ€™s Playful Experiment
Favicon
From Local to Live: How to Deploy Your React Application using Netlify.
Favicon
I wanted my users to have their own subdomain and ...
Favicon
Cost-effective Netlify deployments for large teams using GitHub Actions
Favicon
Automating Netlify Deployments Every 24 Hours with GitHub Actions
Favicon
Best 8 Deployment and Hosting for Next.js App
Favicon
Generating text description from youtube link using Angular, Gemini and Netlify
Favicon
Deploying a Node.js project to Netlify
Favicon
Deploy static site manually using Netlify in 2024
Favicon
Vercel vs Netlify: How to Pick the Right One
Favicon
Integrating Netlify Frontend with External Backend Using Custom DNS Configuration.
Favicon
Seamless Contact Form experience with Netlify Form in Nuxt 3
Favicon
Building a Capital City App With Next.js and Netlify
Favicon
Modificando versรฃo do node na Netlify
Favicon
How I added a guestbook to my website with Clerk, Neon, and Netlify Functions
Favicon
my refine project is deployed but still showing template page
Favicon
Show Your Recently Played Song using Netlify Functions and Last.fm
Favicon
How to Host Website on Netlify for FREE

Featured ones: