├── .eslintcache ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.css ├── App.js ├── Banner.css ├── Banner.js ├── Nav.css ├── Nav.js ├── Row.css ├── Row.js ├── axios.js ├── index.css ├── index.js ├── reportWebVitals.js └── requests.js /.eslintcache: -------------------------------------------------------------------------------- 1 | [{"C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\index.js":"1","C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\App.js":"2","C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\reportWebVitals.js":"3","C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\Row.js":"4","C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\requests.js":"5","C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\axios.js":"6","C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\Banner.js":"7","C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\Nav.js":"8"},{"size":500,"mtime":499162500000,"results":"9","hashOfConfig":"10"},{"size":920,"mtime":1610881132744,"results":"11","hashOfConfig":"10"},{"size":362,"mtime":499162500000,"results":"12","hashOfConfig":"10"},{"size":2049,"mtime":1610911449178,"results":"13","hashOfConfig":"10"},{"size":780,"mtime":1610871658591,"results":"14","hashOfConfig":"10"},{"size":170,"mtime":1610801385143,"results":"15","hashOfConfig":"10"},{"size":1556,"mtime":1610910824625,"results":"16","hashOfConfig":"10"},{"size":1082,"mtime":1610883878178,"results":"17","hashOfConfig":"10"},{"filePath":"18","messages":"19","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"20"},"1x8l7ke",{"filePath":"21","messages":"22","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"20"},{"filePath":"23","messages":"24","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"20"},{"filePath":"25","messages":"26","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"27","messages":"28","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"20"},{"filePath":"29","messages":"30","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"20"},{"filePath":"31","messages":"32","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"33","messages":"34","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"20"},"C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\index.js",[],["35","36"],"C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\App.js",[],"C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\reportWebVitals.js",[],"C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\Row.js",[],"C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\requests.js",[],"C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\axios.js",[],"C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\Banner.js",[],"C:\\Users\\shaon\\OneDrive\\Desktop\\react\\netflix_clone\\src\\Nav.js",[],{"ruleId":"37","replacedBy":"38"},{"ruleId":"39","replacedBy":"40"},"no-native-reassign",["41"],"no-negated-in-lhs",["42"],"no-global-assign","no-unsafe-negation"] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### `npm i react-youtube` 2 | ### `npm i movie-trailer` 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "netflix_clone", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.9", 7 | "@testing-library/react": "^11.2.3", 8 | "@testing-library/user-event": "^12.6.0", 9 | "axios": "^0.21.1", 10 | "movie-trailer": "^2.0.7", 11 | "prop-types": "^15.7.2", 12 | "react": "^17.0.1", 13 | "react-axios": "^2.0.4", 14 | "react-dom": "^17.0.1", 15 | "react-scripts": "4.0.1", 16 | "react-youtube": "^7.13.0", 17 | "web-vitals": "^0.2.4" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": [ 27 | "react-app", 28 | "react-app/jest" 29 | ] 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkshaon/react_netflix_clone/f00c6df8b6b3a3d248e2ef75870870b8344a2984/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Netflix Clone 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkshaon/react_netflix_clone/f00c6df8b6b3a3d248e2ef75870870b8344a2984/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkshaon/react_netflix_clone/f00c6df8b6b3a3d248e2ef75870870b8344a2984/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | } 4 | .app { 5 | background-color: #111; 6 | } 7 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './App.css'; 3 | import Row from './Row'; 4 | import Banner from './Banner'; 5 | import Nav from './Nav'; 6 | import requests from './requests'; 7 | 8 | function App() { 9 | return ( 10 |
11 |
26 | ); 27 | } 28 | 29 | export default App; 30 | -------------------------------------------------------------------------------- /src/Banner.css: -------------------------------------------------------------------------------- 1 | .banner { 2 | color: white; 3 | object-fit: contain; 4 | height: 448px; 5 | } 6 | .banner__contents { 7 | margin-left: 30px; 8 | padding-top: 144px; 9 | height: 190px; 10 | } 11 | .banner__title { 12 | font-size: 3rem; 13 | font-weight: 800; 14 | padding-bottom: 0.3rem; 15 | } 16 | .banner_description { 17 | width: 45rem; 18 | line-height: 1.3; 19 | padding-top: 1rem; 20 | font-size: 0.8rem; 21 | max-width: 360px; 22 | height: 80px; 23 | } 24 | .banner__button { 25 | cursor: pointer; 26 | color: #fff; 27 | outline: none; 28 | border: none; 29 | font-weight: 700; 30 | border-radius: 0.2vw; 31 | padding-left: 2rem; 32 | padding-right: 2rem; 33 | margin-right: 1rem; 34 | padding-top: 0.5rem; 35 | background-color: rgba(51, 51, 51, 0.5); 36 | padding-bottom: 0.5rem; 37 | } 38 | .banner__button:hover { 39 | color: #000; 40 | background-color: #e6e6e6; 41 | transition: all 0.2s; 42 | } 43 | .banner__fadeBottom { 44 | height: 7.4rem; 45 | background-image: linear-gradient( 46 | 180deg, 47 | transparent, 48 | rgba(37, 37, 37, 0.61), 49 | #111 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /src/Banner.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import axios from './axios'; 3 | import requests from './requests'; 4 | import "./Banner.css"; 5 | 6 | function Banner() { 7 | const [movie, setMovie] = useState([]); 8 | 9 | useEffect(() => { 10 | async function fetchData() { 11 | const request = await axios.get(requests.fetchNetflixOriginals); 12 | setMovie(request.data.results[Math.floor(Math.random() * request.data.results.length - 1)]); 13 | // console.log(request.data.results[Math.floor(Math.random() * request.data.results.length - 1)]); 14 | return request; 15 | } 16 | fetchData(); 17 | }, []); 18 | 19 | function truncate(str, n) { 20 | return str?.length > n ? str.substr(0, n-1) + "..." : str; 21 | } 22 | 23 | return( 24 |
34 |
35 |

36 | {movie?.title || movie?.name || movie?.original_name} 37 |

38 |
39 | 40 | 41 |
42 |

43 | {truncate(movie?.overview, 150)} 44 |

45 |
46 |
47 |
48 |
49 | ) 50 | } 51 | 52 | export default Banner; 53 | -------------------------------------------------------------------------------- /src/Nav.css: -------------------------------------------------------------------------------- 1 | .nav { 2 | position: fixed; 3 | top: 0; 4 | display: flex; 5 | z-index: 1; 6 | width: 100%; 7 | height: 30px; 8 | padding: 20px; 9 | justify-content: space-between; 10 | /* animation */ 11 | transition-timing-function: ease-in; 12 | transition: all 0.5s; 13 | } 14 | .nav__black { 15 | background-color: #111; 16 | } 17 | .nav__logo { 18 | position: fixed; 19 | left: 20px; 20 | width: 80px; 21 | object-fit: contain; 22 | } 23 | .nav__avatar { 24 | position: fixed; 25 | right: 20px; 26 | width: 30px; 27 | object-fit: contain; 28 | } 29 | -------------------------------------------------------------------------------- /src/Nav.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import './Nav.css'; 3 | 4 | function Nav() { 5 | const [show, handleShow] = useState(false); 6 | 7 | useEffect(() => { 8 | window.addEventListener("scroll", () => { 9 | if (window.scrollY > 100) { 10 | handleShow(true); 11 | } else { 12 | handleShow(false); 13 | } 14 | }); 15 | return () => { 16 | window.removeEventListener("srcoll"); 17 | } 18 | }, []); 19 | 20 | return( 21 |
22 | netflix logo 27 | firebase logo 33 |
34 | ) 35 | } 36 | 37 | export default Nav; 38 | -------------------------------------------------------------------------------- /src/Row.css: -------------------------------------------------------------------------------- 1 | .row { 2 | color: #fff; 3 | margin-left: 20px; 4 | } 5 | .row__posters { 6 | display: flex; 7 | overflow-y: hidden; 8 | overflow-x: scroll; 9 | padding: 20px; 10 | } 11 | .row__posters::-webkit-scrollbar { 12 | display: none; 13 | } 14 | .row__poster { 15 | object-fit: contain; 16 | width: 100%; 17 | max-height: 100px; 18 | transition: transform 450ms; 19 | margin-right: 10px; 20 | } 21 | .row__poster:hover { 22 | transform: scale(1.08); 23 | } 24 | .row__posterLarge { 25 | max-height: 250px; 26 | } 27 | .row__posterLarge:hover { 28 | transform: scale(1.09); 29 | } 30 | -------------------------------------------------------------------------------- /src/Row.js: -------------------------------------------------------------------------------- 1 | // component 2 | import React, { useState, useEffect } from 'react'; 3 | import instance from './axios'; 4 | import "./Row.css"; 5 | import YouTube from 'react-youtube'; 6 | import movieTrailer from 'movie-trailer'; 7 | 8 | const base_url = "https://image.tmdb.org/t/p/original/"; 9 | 10 | function Row({ title, fetchURL, isLargeRow }) { 11 | const [movies, setMovies] = useState([]); 12 | const [trailerUrl, setTrailerUrl] = useState(""); 13 | 14 | useEffect(() => { 15 | async function fetchData() { 16 | const request = await instance.get(fetchURL); // the url https://api.themoviedb.org/3 + fetchURL 17 | setMovies(request.data.results); 18 | return request; 19 | } 20 | fetchData(); 21 | }, [fetchURL]); 22 | 23 | const opts = { 24 | height: "390", 25 | width: "100%", 26 | playerVars: { 27 | autoplay: 1, 28 | }, 29 | }; 30 | 31 | const handleClick = (movie) => { 32 | if (trailerUrl) { 33 | setTrailerUrl(''); 34 | console.log('trailer closed'); 35 | } else { 36 | console.log('trailer about to open'); 37 | movieTrailer(movie?.name || "") 38 | .then(url => { 39 | console.log('enter trailer'); 40 | const urlParams = new URLSearchParams(new URL(url).search); 41 | setTrailerUrl(urlParams.get('v')); 42 | // console.log(urlParams.get('v')); 43 | }) 44 | .catch(error => console.log(error)); 45 | console.log('hopefully trailer set'); 46 | // function from movie-trailer library, it search movie trailer from youtube 47 | } 48 | } 49 | console.log(trailerUrl); 50 | 51 | return( 52 |
53 |

{title}

54 |
55 | {movies.map((movie) => { 56 | return handleClick(movie)} 60 | src={`${base_url}${isLargeRow ? movie.poster_path : movie.backdrop_path}`} 61 | alt={movie.name} 62 | /> 63 | })} 64 |
65 | {trailerUrl && } 66 |
67 | ) 68 | } 69 | 70 | export default Row; 71 | -------------------------------------------------------------------------------- /src/axios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | // base url of make requests 4 | const instance = axios.create({ 5 | baseURL: "https://api.themoviedb.org/3", 6 | }); 7 | 8 | export default instance; 9 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/requests.js: -------------------------------------------------------------------------------- 1 | // functional module 2 | 3 | // tmdb api key 4 | const API_KEY = "f956f57d239cc98269c50138d916d729"; 5 | 6 | // object of request links 7 | const requests = { 8 | fetchTrending: `/trending/all/week?api_key=${API_KEY}&language=en-US`, 9 | fetchNetflixOriginals: `/discover/tv?api_key=${API_KEY}&with_networks=213`, 10 | fetchTopRated: `/movie/top_rated?api_key=${API_KEY}&language=en-US`, 11 | fetchActionMovies: `/discover/movie?api_key=${API_KEY}&with_genres=28`, 12 | fetchComedyMovies: `/discover/movie?api_key=${API_KEY}&with_genres=35`, 13 | fetchHorrorMovies: `/discover/movie?api_key=${API_KEY}&with_genres=27`, 14 | fetchRomanceMovies: `/discover/movie?api_key=${API_KEY}&with_genres=10749`, 15 | fetchDocumentaries: `/discover/movie?api_key=${API_KEY}&with_genres=99`, 16 | } 17 | 18 | export default requests; 19 | --------------------------------------------------------------------------------