You are on page 1of 41

Jaypee University of Information Technology (JUIT)

Waknaghat, Solan, Himachal Pradesh

“YouTube Master ”
[Project Report – WEB TECH LAB]

Submitted To:

Dr. Sunil Datt Sharma

Submitted By:

Aayush Sharma [211193]


1 | Abstract
This report, entitled “YouTube Master”, contains a brief introduction to the different techniques
used for making this project their implementation. This report is put together by me as the
fulfilment to the lab project assigned by Dr.Sunil Datt Sharma.

2 | Acknowledgment
I hereby acknowledge that the material used/written in this report is not, wholly or partially,
plagiarized; proper references have been mentioned where we used external
printed/written/published material, and the instructor has the right to reject a portion of the report
or the report altogether if he finds the fact to be something else than what has been mentioned
above.

I have tried my level best to keep the references correct and the facts & figures up-to-date, but
being human, any error or lapses are expected and are requested to be tolerated.

Aayush Sharma
[CSE/211193]

Page No: 1
Table of Contents

TOPIC PAGE
1 Abstract 1
2 Acknowledgment 1
3 “YouTube” – AN INTRODUCTION 3
4 Technologies Used 4
4.1 HTML 4
4.2 JavaScript 4
4.3 TAILWIND CSS 4
5 Requirements Analysis 5
6 System Design: Hierarchy of react components 6
7 System Architecture 9
7.1.1 Statistics 9
7.1.2 Tech Stack 9
8 Entity Relationship Diagram 10
8.1 ER Diagram and Introduction 10
8.2 ER Diagram for project 11
9 Break Down of the Project 12
9.1 Layout of the YouTube Master 13
9.1.1 Header Section Source Code 13
9.1.2 Tailwind CSS Source Code 17
9.1.3 Left Navigation Bar and menu Source code 19
Feed Source Code including video card
9.1.4 and video length 24
Video Details and video suggestion
9.1.5 Source Code 28
Search Results and Search Result Video
9.1.6 Source Code 34
API Calling Source Code
9.1.7 38
9.2 Final Output Of The Project 39
10 Future Work And Conclusion 39
10.1 Conclusion 39
10.2 Future Work 40
11 References 40

Page No: 2
3 | Introduction

YouTube is a global online video sharing and social media platform headquartered in
San Bruno, California. It was launched on February 14, 2005, by Steve Chen, Chad
Hurley, and Jawed Karim. It is owned by Google and is the second most visited website,
after Google Search. YouTube has more than 2.5 billion monthly users, who collectively
watch more than one billion hours of videos each day. As of May 2019, videos were
being uploaded at a rate of more than 500 hours of content per minute.

In October 2006, YouTube was bought by Google for $1.65 billion. Google's ownership
of YouTube expanded the site's business model, expanding from generating revenue
from advertisements alone to offering paid content such as movies and exclusive
content produced by YouTube. It also offers YouTube Premium, a paid subscription
option for watching content without ads. YouTube also approved creators to
participate in Google's AdSense program, which seeks to generate more revenue for
both parties. YouTube reported revenue of $29.2 billion in 2022. In 2021, YouTube's
annual advertising revenue increased to $28.8 billion, an increase in revenue of 9
billion from the previous year.

Since its purchase by Google, YouTube has expanded beyond the core website into
mobile apps, network television, and the ability to link with other platforms. Video
categories on YouTube include music videos, video clips, news, short films, feature
films, songs, documentaries, movie trailers, teasers, live streams, vlogs, and more.
Most content is generated by individuals, including collaborations between YouTubers
and corporate sponsors.

Page No: 3
4 | Technologies Used

HTML:

HTML is the standard markup language for creating Web pages. It stands for HyperText
Markup Language. It describes the structure of a Web page. It consists of a series of
elements. HTML elements tell the browser how to display the content. HTML elements
are represented by tags. HTML tags label pieces of content such as "heading",
"paragraph", "table", and so on. Browsers do not display the HTML tags, but use them
to render the content of the page.

JAVASCRIPT:

JavaScript(often shortened to JS) is a lightweight, interpreted, object-oriented


language withfirst-class functions, and is best known as the scripting language for Web
pages, but it's usedin many non-browser environments as well. It is a prototype-based,
multi-paradigm scripting language that is dynamic, and supports object-oriented,
imperative, and functional programming styles. JavaScript runs on the client side of
the web, which can be used to design / program how the web pages behave on the
occurrence of an event. JavaScript is an easy to learn and also powerful scripting
language, widely used for controlling web page behavior.

TAILWIND CSS:

Tailwind CSS is an open source CSS framework. The main feature of this library is that, unlike
other CSS frameworks like Bootstrap, it does not provide a series of predefined classes for
elements such as buttons or tables.

Page No: 4
5 | REQUIREMENTS ANALYSIS

Node js and npm (node package manager):

Node.js actually provides a runtime environment to execute JavaScript code from


outside a browser. NPM, the default package manager for Node Js is used for managing
and sharing the packages for any JavaScript projects. React uses Node.js and NPM for
the management of dependencies and runtime.

We can install node js form the official website https://nodejs.org

Then we just have to follow the instructions on the installation dialog box and node js
is installed. To check if the installation was successful or not you can run this command
in yourterminal/command prompt:

node –v and npm –v

To fetch api data from Rapid API:

➢ Head over to the Rapid API website


➢ Search for the Youtube Api by Glavier
➢ Subsribe for the free plan and click on Test Endpoint
➢ After subscribing click on Code Snippets
➢ Copy your api key in api.js
➢ Call the api in your code

Run the project:

To run the project we need to write the command npm start it automatically opens in
a default browser at http:localhost:3000 by default, and whenever you change
something in the code and save it it automatically reloads the page and renders the
new code

Page No: 5
6 | System Design: Hierarchy of react components

This mini project is divided into a set of react components. The App.js component renders two
components namely Header and Video Module. The header component has a fixed header
styling with a input search type textbox and having two buttons i.e. search and sort on the
right of it. The search button allows you to search for videos based on your query typed in the
search field. And a sort button helps you to sort the results based on a few factors like:
relevance, date, rating, title, video count and view count.

Page No: 6
Page No: 7
Source Code for App.js:

import React from "react";


import { BrowserRouter, Routes, Route } from "react-router-dom";

import Header from "./components/Header";


import Feed from "./components/Feed";
import SearchResult from "./components/SearchResult";
import VideoDetails from "./components/VideoDetails";
import { AppContext } from "./context/contextApi";

const App = () => {


return (
<AppContext>
<BrowserRouter>
<div className="flex flex-col h-full">
<Header />
<Routes>
<Route path="/" exact element={<Feed />} />
<Route
path="/searchResult/:searchQuery"
element={<SearchResult />}
/>
<Route path="/video/:id" element={<VideoDetails />} />
</Routes>
</div>
</BrowserRouter>
</AppContext>
);
};

export default App;

Page No: 8
7 | System Architecture

Since the original architecture of Youtube is complex and implementing is out of scope for the
current scenario, we will be discussing the differences of both the systems

7.1 Existing system

7.1.1 Statistics :
• 4 billion views a day
• 60 hours of video is uploaded every minute
• 350+ million devices are YouTube enabled
• Revenue doubled in 2010
• 1 million lines of Python code

7.1.2 Tech Stack

Python - most of the lines of code for YouTube are still in Python. Every time
• you watch a YouTube video you are executing a bunch of Python code.

• Apache - Apache keeps Youtube simple. Every request goes through Apache.

Linux - The benefit of Linux is there’s always a way to get in and see how your
• systemis behaving. No matter how bad your app is behaving, you can take
a look at it with Linux tools like strace and tcpdump.
MySQL - is used a lot. When you watch a video you are getting data from

MySQL. Sometimes it’s used a relational database or a blob store. It’s about
tuning and making choices about how you organize your data.

These are some of the technologies which Youtube uses in their current system. They also use
Zookeeper (distributed lock system), Vitess (frontend for SQL), and other templating engines
to keep the app up and running.

Page No: 9
8 | Entity Relationship Diagram

8.1 ER Diagram and Introduction

An E-R model is usually the result of systematic analysis to define and describe
what is important to processes in an area of a business. It does not define the business
processes; it only presents a business data schema in graphical form. It is usually
drawn in a graphical form as boxes (entities) that are connected by lines (relationships)
which express the associations and dependencies between entities. An ER model can
also be expressed in a verbal form, for example: one building may be divided into zero
or more apartments, but one apartment can only be located in one building.

Entities may be characterized not only by relationships, but also by additional


properties (attributes), which include identifiers called "primary keys". Diagrams
created to represent attributes as well as entities and relationships may be called
entity-attribute-relationship dia- grams, rather than entity–relationship models.

An ER model is typically implemented as a database. In a simple relational database


imple-mentation, each row of a table represents one instance of an entity type,
and each field in a table represents an attribute type. In a relational database a
relationship between entities is implemented by storing the primary key of one entity
as a pointer or "foreign key" in the table of another entity.

There is a tradition for ER/data models to be built at two or three levels of


abstraction. Notethat the conceptual-logical-physical hierarchy below is used in other
kinds of specification, andis different from the three schema approach to software
engineering.

Page No: 10
8.2 ER Diagram for project

Page No: 11
9 |Break Down of the project

The Header contains three sections (left, center and right). The left section contains
the logo and menu, the center contains the search box and an icon, and the right
contains navigationicons. The icons are based on similar elements, which means that I
design an icon elementand then copy, paste, and edit it to create the others

The Main body contains two sections (sidebar and content). The navigation links in the
sidebar are all working and are made in similar way. The same thing happens to the
videos in the content section.

Page No: 12
9.1 Layout of the YouTube Master

9.1.1 Header Section Source Code

import React, { useContext, useState } from "react";


import { Link, useLocation, useNavigate } from "react-router-dom";

import ytLogo from "../images/yt-logo.png";


import ytLogoMobile from "../images/yt-logo-mobile.png";

import { SlMenu } from "react-icons/sl";


import { IoIosSearch } from "react-icons/io";
import { RiVideoAddLine } from "react-icons/ri";
import { FiBell } from "react-icons/fi";
import { CgClose } from "react-icons/cg";

import { Context } from "../context/contextApi";


import Loader from "../shared/loader";
import profilepic from "../images/profile-pic.png"

const Header = () => {


const [searchQuery, setSearchQuery] = useState("");

const { loading, mobileMenu, setMobileMenu } = useContext(Context);

const navigate = useNavigate();

const searchQueryHandler = (event) => {


if (
(event?.key === "Enter" || event === "searchButton") &&
searchQuery?.length > 0
){
navigate(`/searchResult/${searchQuery}`);
}

Page No: 13
};

const mobileMenuToggle = () => {


setMobileMenu(!mobileMenu);
};

const { pathname } = useLocation();


const pageName = pathname?.split("/")?.filter(Boolean)?.[0];

return (
<div className="sticky top-0 z-10 flex flex-row items-center justify-between h-14
px-4 md:px-5 bg-black dark:bg-black">
{loading && <Loader />}

<div className="flex h-5 items-center">


{pageName !== "video" && (
<div
className="flex md:hidden md:mr-6 cursor-pointer items-center justify-
center h-10 w-10 rounded-full hover:bg-[#303030]/[0.6]"
onClick={mobileMenuToggle}
>
{mobileMenu ? (
<CgClose className="text-white text-xl" />
):(
<SlMenu className="text-white text-xl" />
)}
</div>
)}
<Link to="/" className="flex h-5 items-center">
<img
className="h-full hidden dark:md:block"
src={ytLogo}
alt="Youtube"

/>

Page No: 14
<img
className="h-full md:hidden"
src={ytLogoMobile}
alt="Youtube"
/>
</Link>
</div>
<div className="group flex items-center">
<div className="flex h-8 md:h-10 md:ml-10 md:pl-5 border border-[#303030]
rounded-l-3xl group-focus-within:border-blue-500 md:group-focus-within:ml-5
md:group-focus-within:pl-0">
<div className="w-10 items-center justify-center hidden group-focus-
within:md:flex">
<IoIosSearch className="text-white text-xl" />
</div>
<input
type="text"
className="bg-transparent outline-none text-white pr-5 pl-5 md:pl-0 w-
44 md:group-focus-within:pl-0 md:w-64 lg:w-[500px]"
onChange={(e) => setSearchQuery(e.target.value)}
onKeyUp={searchQueryHandler}
placeholder="Search"
value={searchQuery}
/>
</div>
<button
className="w-[40px] md:w-[60px] h-8 md:h-10 flex items-center justify-
center border border-l-0 border-[#303030] rounded-r-3xl bg-white/[0.1]"
onClick={() => searchQueryHandler("searchButton")}
>
<IoIosSearch className="text-white text-xl" />
</button>
</div>
<div className="flex items-center">
<div className="hidden md:flex">
<div className="flex items-center justify-center h-10 w-10 rounded-full

Page No: 15
hover:bg-[#303030]/[0.6]">
<RiVideoAddLine className="text-white text-xl cursor-pointer" />
</div>
<div className="flex items-center justify-center ml-2 h-10 w-10 rounded-
full hover:bg-[#303030]/[0.6]">
<FiBell className="text-white text-xl cursor-pointer" />
</div>
</div>
<div className="flex h-8 w-8 overflow-hidden rounded-full md:ml-4">
<img src={profilepic} />
</div>
</div>
</div>
);
};

export default Header;

OUTPUT

Page No: 16
9.1.2 Tailwind CSS Source Code

@tailwind base;
@tailwind components;
@tailwind utilities;

body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
"Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

#root {
height: 100vh;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}

.load-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
background-color: #fdba2c;
}
.bar {
content: "";

Page No: 17
display: inline;
position: absolute;
width: 0;
height: 100%;
left: 50%;
text-align: center;
}
.bar:nth-child(1) {
background-color: #da4733;
animation: loading 3s linear infinite;
}
.bar:nth-child(2) {
background-color: #3b78e7;
animation: loading 3s linear 1s infinite;
}
.bar:nth-child(3) {
background-color: #fdba2c;
animation: loading 3s linear 2s infinite;
}
@keyframes loading {
from {
left: 50%;
width: 0;
z-index: 100;
}
33.3333% {
left: 0;
width: 100%;
z-index: 10;
}
to {
left: 0;
width: 100%;
}

Page No: 18
}

.custom-h {
height: auto !important;
}
@media screen and (min-width: 1024px) {
.custom-h {
height: 100vh !important;
}
}

9.1.3 Left Navigation Bar and menu Source code

import React, { useContext } from "react";


import { useNavigate } from "react-router-dom";

import LeftNavMenuItem from "./LeftNavMenuItem";


import { categories } from "../utils/constants";
import { Context } from "../context/contextApi";

const LeftNav = () => {


const { selectedCategory, setSelectedCategory, mobileMenu } =
useContext(Context);

const navigate = useNavigate();

const clickHandler = (name, type) => {


switch (type) {
case "category":
return setSelectedCategory(name);
case "home":
return setSelectedCategory(name);
case "menu":
return false;
default:

Page No: 19
break;
}
};

return (
<div
className={`md:block w-[240px] overflow-y-auto h-full py-4 bg-black absolute
md:relative z-10 translate-x-[-240px] md:translate-x-0 transition-all ${
mobileMenu ? "translate-x-0" : ""
}`}
>
<div className="flex px-5 flex-col">
{categories.map((item) => {
return (
<React.Fragment key={item.name}>
<LeftNavMenuItem
text={item.type === "home" ? "Home" : item.name}
icon={item.icon}
action={() => {
clickHandler(item.name, item.type);
navigate("/");
}}
className={`${
selectedCategory === item.name
? "bg-white/[0.15]"
: ""
}`}
/>
{item.divider && (
<hr className="my-5 border-white/[0.2]" />
)}
</React.Fragment>
);
})}
<hr className="my-5 border-white/[0.2]" />

Page No: 20
<div className="text-white/[0.5] text-[12px]">
Site by : Aayush Sharma 211193
</div>
</div>
</div>
);
};

export default LeftNav;

Menu Source code

import React from "react";

const LeftNavMenuItem = ({ text, icon, className, action }) => {


return (
<div
className={
"text-white text-sm cursor-pointer h-10 flex items-center px-3 mb-[1px] rounded-lg
hover:bg-white/[0.15] " +
className
}
onClick={action}
>
<span className="text-xl mr-5">{icon}</span>
{text}
</div>
);
};

export default LeftNavMenuItem;

Page No: 21
icons Source code

//created left navigation bar icons

import React from "react";


import { AiFillHome, AiOutlineFlag } from "react-icons/ai";
import { MdLocalFireDepartment, MdLiveTv } from "react-icons/md";
import { CgMusicNote } from "react-icons/cg";
import { FiFilm } from "react-icons/fi";
import { IoGameControllerSharp } from "react-icons/io5";
import { ImNewspaper } from "react-icons/im";
import { GiDiamondTrophy, GiEclipse } from "react-icons/gi";
import { RiLightbulbLine, RiFeedbackLine } from "react-icons/ri";
import { FiSettings, FiHelpCircle } from "react-icons/fi";

export const categories = [


{ name: "New", icon: <AiFillHome />, type: "home" },
{ name: "Trending", icon: <MdLocalFireDepartment />, type: "category" },
{ name: "Music", icon: <CgMusicNote />, type: "category" },
{ name: "Films", icon: <FiFilm />, type: "category" },
{ name: "Live", icon: <MdLiveTv />, type: "category" },
{ name: "Gaming", icon: <IoGameControllerSharp />, type: "category" },
{ name: "News", icon: <ImNewspaper />, type: "category" },
{ name: "Sports", icon: <GiDiamondTrophy />, type: "category" },
{ name: "Learning", icon: <RiLightbulbLine />, type: "category" },
{
name: "Fashion & beauty",
icon: <GiEclipse />,
type: "category",
divider: true,
},
{ name: "Settings", icon: <FiSettings />, type: "menu" },
{ name: "Report History", icon: <AiOutlineFlag />, type: "menu" },

Page No: 22
{ name: "Help", icon: <FiHelpCircle />, type: "menu" },
{ name: "Send feedback", icon: <RiFeedbackLine />, type: "menu" },
];

OUTPUT

Page No: 23
9.1.4 Feed Source Code including video card and video length

import React, { useContext, useEffect } from "react";

import { Context } from "../context/contextApi";


import LeftNav from "./LeftNav";
import VideoCard from "./VideoCard";

const Feed = () => {


const { loading, searchResults } = useContext(Context);

useEffect(() => {
document.getElementById("root").classList.remove("custom-h");
}, []);

return (
<div className="flex flex-row h-[calc(100%-56px)]">
<LeftNav />
<div className="grow w-[calc(100%-240px)] h-full overflow-y-auto bg-black">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4
gap-4 p-5">
{!loading &&
searchResults.map((item) => {
if (item.type !== "video") return false;//if video not there from server
return (
<VideoCard//map loop
key={item?.video?.videoId}//optional chainig for preventing app
crash
video={item?.video}
/>
);
})}
</div>
</div>
</div>

Page No: 24
);
};

export default Feed;

Video Card Source Code

import React from "react";


import { abbreviateNumber } from "js-abbreviation-number";
import { Link } from "react-router-dom";
import { BsFillCheckCircleFill } from "react-icons/bs";

import VideoLength from "../shared/videoLength";


const VideoCard = ({ video }) => {
return (
<Link to={`/video/${video?.videoId}`}>
<div className="flex flex-col mb-8">
<div className="relative h-48 md:h-40 md:rounded-xl overflow-hidden">
<img
className="h-full w-full object-cover"
src={video?.thumbnails[0]?.url}
/>
{video?.lengthSeconds && (
<VideoLength time={video?.lengthSeconds} />
)}
</div>
<div className="flex text-white mt-3">
<div className="flex items-start">
<div className="flex h-9 w-9 rounded-full overflow-hidden">
<img
className="h-full w-full object-cover"
src={video?.author?.avatar[0]?.url}
/>
</div>

Page No: 25
</div>
<div className="flex flex-col ml-3 overflow-hidden">
<span className="text-sm font-bold line-clamp-2">
{video?.title}
</span>
<span className="text-[12px] font-semibold mt-2 text-white/[0.7] flex
items-center">
{video?.author?.title}
{video?.author?.badges[0]?.type ===
"VERIFIED_CHANNEL" && (
<BsFillCheckCircleFill className="text-white/[0.5] text-[12px] ml-1"
/>
)}
</span>
<div className="flex text-[12px] font-semibold text-white/[0.7]
truncate overflow-hidden">
<span>{`${abbreviateNumber(
video?.stats?.views,
2
)} views`}</span>
<span className="flex text-[24px] leading-none font-bold text-
white/[0.7] relative top-[-10px] mx-1">
.
</span>
<span className="truncate">
{video?.publishedTimeText}
</span>
</div>
</div>
</div>
</div>
</Link>
);
};

export default VideoCard;

Page No: 26
Video Length Source Code

import React from "react";


import moment from "moment";

const VideoLength = ({ time }) => {


const videoLengthInSeconds = moment()
?.startOf("day")
?.seconds(time)
?.format("H:mm:ss");
return (
<span className="absolute bottom-2 right-2 bg-black py-1 px-2 text-white text-
xs rounded-md">
{videoLengthInSeconds}
</span>
);
};
export default VideoLength;

OUTPUT

Page No: 27
9.1.5 Video Details and video suggestion Source Code

Video Details Source Code


import React, { useState, useEffect, useContext } from "react";
import { useParams } from "react-router-dom";
import ReactPlayer from "react-player/youtube";
import { BsFillCheckCircleFill } from "react-icons/bs";
import { AiOutlineLike } from "react-icons/ai";
import { abbreviateNumber } from "js-abbreviation-number";

import { fetchDataFromApi } from "../utils/api";


import { Context } from "../context/contextApi";
import SuggestionVideoCard from "./SuggestionVideoCard";

const VideoDetails = () => {


const [video, setVideo] = useState();
const [relatedVideos, setRelatedVideos] = useState();
const { id } = useParams();//GIve Video Id
const { setLoading } = useContext(Context);

useEffect(() => {
document.getElementById("root").classList.add("custom-h");
fetchVideoDetails();
fetchRelatedVideos();
}, [id]);

const fetchVideoDetails = () => {//getting Video data through video id


setLoading(true);
fetchDataFromApi(`video/details/?id=${id}`).then((res) => {
console.log(res);
setVideo(res);
setLoading(false);
});
};

Page No: 28
const fetchRelatedVideos = () => {
setLoading(true);
fetchDataFromApi(`video/related-contents/?id=${id}`).then((res) => {
console.log(res);
setRelatedVideos(res);
setLoading(false);
});
};

return (
<div className="flex justify-center flex-row h-[calc(100%-56px)] bg-black">
<div className="w-full max-w-[1280px] flex flex-col lg:flex-row">
<div className="flex flex-col lg:w-[calc(100%-350px)] xl:w-[calc(100%-
400px)] px-4 py-3 lg:py-6 overflow-y-auto">
<div className="h-[200px] md:h-[400px] lg:h-[400px] xl:h-[550px] ml-[-
16px] lg:ml-0 mr-[-16px] lg:mr-0">
<ReactPlayer
url={`https://www.youtube.com/watch?v=${id}`}
controls
width="100%"
height="100%"
style={{ backgroundColor: "#000000" }}
playing={true}
/>
</div>
<div className="text-white font-bold text-sm md:text-xl mt-4 line-clamp-
2">
{video?.title}
</div>
<div className="flex justify-between flex-col md:flex-row mt-4">
<div className="flex">
<div className="flex items-start">
<div className="flex h-11 w-11 rounded-full overflow-hidden">
<img

Page No: 29
className="h-full w-full object-cover"
src={video?.author?.avatar[0]?.url}//author icon
/>
</div>
</div>
<div className="flex flex-col ml-3">
<div className="text-white text-md font-semibold flex items-
center">
{video?.author?.title}
{video?.author?.badges[0]?.type ===
"VERIFIED_CHANNEL" && (
<BsFillCheckCircleFill className="text-white/[0.5] text-[12px]
ml-1" />
)}
</div>
<div className="text-white/[0.7] text-sm">
{video?.author?.stats?.subscribersText}
</div>
</div>
</div>
<div className="flex text-white mt-4 md:mt-0">
<div className="flex items-center justify-center h-11 px-6 rounded-
3xl bg-white/[0.15]">
<AiOutlineLike className="text-xl text-white mr-2" />
{`${abbreviateNumber(
video?.stats?.views,
2
)} Likes`}
</div>
<div className="flex items-center justify-center h-11 px-6 rounded-
3xl bg-white/[0.15] ml-4">
{`${abbreviateNumber(
video?.stats?.views,
2
)} Views`}

Page No: 30
</div>
</div>
</div>
</div>
<div className="flex flex-col py-6 px-4 overflow-y-auto lg:w-[350px] xl:w-
[400px]">
{relatedVideos?.contents?.map((item, index) => {
if (item?.type !== "video") return false;
return (
<SuggestionVideoCard
key={index}
video={item?.video}
/>
);
})}
</div>
</div>
</div>
);
};

export default VideoDetails;

Video Suggestion Source Code

import React from "react";


import { abbreviateNumber } from "js-abbreviation-number";
import { BsFillCheckCircleFill } from "react-icons/bs";
import { Link } from "react-router-dom";

import VideoLength from "../shared/videoLength";

const SuggestionVideoCard = ({ video }) => {


return (
<Link to={`/video/${video?.videoId}`}>

Page No: 31
<div className="flex mb-3">
<div className="relative h-24 lg:h-20 xl:h-24 w-40 min-w-[168px] lg:w-32
lg:min-w-[128px] xl:w-40 xl:min-w-[168px] rounded-xl bg-slate-800 overflow-hidden">
<img
className="h-full w-full object-cover"
src={video?.thumbnails[0]?.url}
/>
{video?.lengthSeconds && (
<VideoLength time={video?.lengthSeconds} />
)}
</div>
<div className="flex flex-col ml-3 overflow-hidden">
<span className="text-sm lg:text-xs xl:text-sm font-bold line-clamp-2
text-white">
{video?.title}
</span>
<span className="text-[12px] lg:text-[10px] xl:text-[12px] font-semibold
mt-2 text-white/[0.7] flex items-center">
{video?.author?.title}
{video?.author?.badges[0]?.type ===
"VERIFIED_CHANNEL" && (
<BsFillCheckCircleFill className="text-white/[0.5] text-[12px] lg:text-
[10px] xl:text-[12px] ml-1" />
)}
</span>
<div className="flex text-[12px] lg:text-[10px] xl:text-[12px] font-
semibold text-white/[0.7] truncate overflow-hidden">
<span>{`${abbreviateNumber(
video?.stats?.views,
2
)} views`}</span>
<span className="flex text-[24px] leading-none font-bold text-
white/[0.7] relative top-[-10px] mx-1">
.
</span>
<span className="truncate">

Page No: 32
{video?.publishedTimeText}
</span>
</div>
</div>
</div>
</Link>
);
};

export default SuggestionVideoCard;

OUTPUT

Page No: 33
9.1.6 Search Results and Search Result Video Source Code

Search Results Source Code

import React, { useState, useEffect, useContext } from "react";


import { useParams } from "react-router-dom";
import { fetchDataFromApi } from "../utils/api";
import { Context } from "../context/contextApi";
import LeftNav from "./LeftNav";
import SearchResultVideoCard from "./SearchResultVideoCard";

const SearchResult = () => {


const [result, setResult] = useState();
const { searchQuery } = useParams();//Search by Query
const { setLoading } = useContext(Context);

useEffect(() => {
document.getElementById("root").classList.remove("custom-h");
fetchSearchResults();
}, [searchQuery]);

const fetchSearchResults = () => {


setLoading(true);
fetchDataFromApi(`search/?q=${searchQuery}`).then((res) => {
console.log(res);
setResult(res?.contents);
setLoading(false);
});
};

return (
<div className="flex flex-row h-[calc(100%-56px)]">
<LeftNav />
<div className="grow w-[calc(100%-240px)] h-full overflow-y-auto bg-black">
<div className="grid grid-cols-1 gap-2 p-5">

Page No: 34
{result?.map((item) => {
if (item?.type !== "video") return false;
let video = item.video;
return (
<SearchResultVideoCard
key={video.videoId}
video={video}
/>
);
})}
</div>
</div>
</div>
);
};

export default SearchResult;

Search Results Video Source Code

import React from "react";


import { Link } from "react-router-dom";
import { abbreviateNumber } from "js-abbreviation-number";
import { BsFillCheckCircleFill } from "react-icons/bs";

import VideoLength from "../shared/videoLength";

const SearchResultVideoCard = ({ video }) => {


return (
<Link to={`/video/${video?.videoId}`}>
<div className="flex flex-col md:flex-row mb-8 md:mb-3 lg:hover:bg-
white/[0.1] rounded-xl md:p-4">
<div className="relative flex shrink-0 h-48 md:h-28 lg:h-40 xl:h-48 w-full
md:w-48 lg:w-64 xl:w-80 rounded-xl bg-slate-800 overflow-hidden">
<img

Page No: 35
className="h-full w-full object-cover"
src={video?.thumbnails[0]?.url}
/>
{video?.lengthSeconds && (
<VideoLength time={video?.lengthSeconds} />
)}
</div>
<div className="flex flex-col ml-4 md:ml-6 mt-4 md:mt-0 overflow-hidden">
<span className="text-lg md:text-2xl font-semibold line-clamp-2 text-
white">
{video?.title}
</span>
<span className="empty:hidden text-sm line-clamp-1 md:line-clamp-2
text-white/[0.7] md:pr-24 md:my-4">
{video?.descriptionSnippet}
</span>
<div className="hidden md:flex items-center">
<div className="flex items-start mr-3">
<div className="flex h-9 w-9 rounded-full overflow-hidden">
<img
className="h-full w-full object-cover"
src={video?.author?.avatar[0]?.url}
/>
</div>
</div>
<div className="flex flex-col">
<span className="text-sm font-semibold mt-2 text-white/[0.7] flex
items-center">
{video?.author?.title}
{video?.author?.badges[0]?.type ===
"VERIFIED_CHANNEL" && (
<BsFillCheckCircleFill className="text-white/[0.5] text-[12px]
lg:text-[10px] xl:text-[12px] ml-1" />
)}
</span>

Page No: 36
<div className="flex text-sm font-semibold text-white/[0.7] truncate
overflow-hidden">
<span>{`${abbreviateNumber(
video?.stats?.views,
2
)} views`}</span>
<span className="flex text-[24px] leading-none font-bold text-
white/[0.7] relative top-[-10px] mx-1">
.
</span>
<span className="truncate">
{video?.publishedTimeText}
</span>
</div>
</div>
</div>
</div>
</div>
</Link>
);
};

export default SearchResultVideoCard;

Page No: 37
OUTPUT

9.1.7 Api Calling Source Code


import axios from "axios";
const BASE_URL = "https://youtube138.p.rapidapi.com";
const options = {
params: { hl: "en", gl: "US" },
headers: {
"X-RapidAPI-Key":
process.env.REACT_APP_YOUTUBE_API_KEY ||
"524165dab1msh2846d596af99f12p184658jsn8a588705ff94",
"X-RapidAPI-Host": "youtube138.p.rapidapi.com",
},
};
export const fetchDataFromApi = async (url) => {
const { data } = await axios.get(`${BASE_URL}/${url}`, options);
return data;
};

Page No: 38
9.2 Final Output of the project

10 | Conclusion and Future work:

10.1 Conclusion

Building this project helped me solidify my knowledge in the main concepts of


HTML,JSS,CSS. This project will include React components, lifecycle methods, state
management, passing props from parent to child components, API request and much
more. Thus, it was a great overall learning experience.

Page No: 39
10.2 Future Work

The future work for this react app will be including major features of Youtube such as:

1. Home feed with infinite scroll.

2. Trending videos.

3. YouTube landing page.

4. Displaying comments and video details.

5. Having create your own video feature.

11 | REFERENCES:

Web Reference:

1. https://developers.google.com/youtube/v3

2. https://scrimba.com/g/glearnreact

3. https://www.techomoro.com/how-to-install-and-setup-a-react-app-
on-windows-10/

4. https://scrimba.com/g/gintrotojavascript

5. https://reactjs.org/docs/faq-internals.html

6. https://developers.google.com/apis-explorer

7. https://scrimba.com/g/glearnreact

Page No: 40

You might also like