53 | {/* Empty fade div with bottom fade effect */}
54 |
55 |
56 | );
57 | }
58 |
59 | export default Banner;
60 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/Banner.css:
--------------------------------------------------------------------------------
1 | /* Positioning banner and color */
2 | .banner {
3 | color:white;
4 | object-fit: contain;
5 | height: 448px;
6 | }
7 |
8 | /* Positioning banner content */
9 | .banner__contents {
10 | margin-left: 30px;
11 | padding-top:140px;
12 | height: 190px;
13 | }
14 | /* Styling banner title */
15 | .banner__title {
16 | font-size: 3rem;
17 | font-weight: 800;
18 | padding-bottom: 0.3rem;;
19 | }
20 |
21 | /* Styling banner description font*/
22 | .banner__description {
23 | width: 45rem;
24 | line-height: 1.3;
25 | padding-top:1rem;
26 | font-size:0.8rem;
27 | max-width: 360px;
28 | height:80px;
29 | }
30 |
31 | /* Styling buttons */
32 | .banner__button {
33 | cursor:pointer;
34 | color:#fff;
35 | outline: none;
36 | border: none;
37 | font-weight: 700;
38 | border-radius: 0.2vw;
39 | padding-left: 2rem;
40 | padding-right: 2rem;
41 | margin-right: 1rem;
42 | padding-top:0.5rem;
43 | padding-bottom: 0.5rem;
44 | background-color: rgba(51, 51, 51, 0.5);
45 | }
46 |
47 | /* Styling hover effect */
48 | .banner__button:hover {
49 | color:#000;
50 | background-color: #e6e6e6;
51 | transition: all 0.2s;
52 | }
53 |
54 | /* Creates a fade in banner bottom effect */
55 | .banner--fadeBottom {
56 | height: 7.4rem;
57 | background-image: linear-gradient(180deg, transparent, rgba(37, 37, 37, 0.61), #111)
58 | }
59 |
60 | .banner__button11 {
61 | cursor:pointer;
62 | color:#fff;
63 | outline: none;
64 | border: none;
65 | font-weight: 700;
66 | border-radius: 0.2vw;
67 | padding-left: 2rem;
68 | padding-right: 2rem;
69 | margin-right: 1rem;
70 | padding-top:0.5rem;
71 | padding-bottom: 0.5rem;
72 | background-color: rgba(51, 51, 51, 0.5);
73 | }
74 |
75 | .banner__button111 {
76 | cursor:pointer;
77 | color:#fff;
78 | outline: none;
79 | border: none;
80 | font-weight: 700;
81 | border-radius: 0.2vw;
82 | padding-left: 2rem;
83 | padding-right: 2rem;
84 | margin-right: 1rem;
85 | padding-top:0.5rem;
86 | padding-bottom: 0.5rem;
87 | background-color: rgba(51, 51, 51, 0.5);
88 | }
89 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '30 6 * * 6'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'java', 'javascript' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v1
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v1
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v1
71 |
--------------------------------------------------------------------------------
/.github/workflows/google.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a docker container, publish it to Google Container Registry, and deploy it to GKE when there is a push to the master branch.
2 | #
3 | # To configure this workflow:
4 | #
5 | # 1. Ensure that your repository contains the necessary configuration for your Google Kubernetes Engine cluster, including deployment.yml, kustomization.yml, service.yml, etc.
6 | #
7 | # 2. Set up secrets in your workspace: GKE_PROJECT with the name of the project and GKE_SA_KEY with the Base64 encoded JSON service account key (https://github.com/GoogleCloudPlatform/github-actions/tree/docs/service-account-key/setup-gcloud#inputs).
8 | #
9 | # 3. Change the values for the GKE_ZONE, GKE_CLUSTER, IMAGE, and DEPLOYMENT_NAME environment variables (below).
10 | #
11 | # For more support on how to run the workflow, please visit https://github.com/google-github-actions/setup-gcloud/tree/master/example-workflows/gke
12 |
13 | name: Build and Deploy to GKE
14 |
15 | on:
16 | push:
17 | branches:
18 | - master
19 |
20 | env:
21 | PROJECT_ID: ${{ secrets.GKE_PROJECT }}
22 | GKE_CLUSTER: cluster-1 # TODO: update to cluster name
23 | GKE_ZONE: us-central1-c # TODO: update to cluster zone
24 | DEPLOYMENT_NAME: gke-test # TODO: update to deployment name
25 | IMAGE: static-site
26 |
27 | jobs:
28 | setup-build-publish-deploy:
29 | name: Setup, Build, Publish, and Deploy
30 | runs-on: ubuntu-latest
31 | environment: production
32 |
33 | steps:
34 | - name: Checkout
35 | uses: actions/checkout@v2
36 |
37 | # Setup gcloud CLI
38 | - uses: google-github-actions/setup-gcloud@v0.2.0
39 | with:
40 | service_account_key: ${{ secrets.GKE_SA_KEY }}
41 | project_id: ${{ secrets.GKE_PROJECT }}
42 |
43 | # Configure Docker to use the gcloud command-line tool as a credential
44 | # helper for authentication
45 | - run: |-
46 | gcloud --quiet auth configure-docker
47 |
48 | # Get the GKE credentials so we can deploy to the cluster
49 | - uses: google-github-actions/get-gke-credentials@v0.2.1
50 | with:
51 | cluster_name: ${{ env.GKE_CLUSTER }}
52 | location: ${{ env.GKE_ZONE }}
53 | credentials: ${{ secrets.GKE_SA_KEY }}
54 |
55 | # Build the Docker image
56 | - name: Build
57 | run: |-
58 | docker build \
59 | --tag "gcr.io/$PROJECT_ID/$IMAGE:$GITHUB_SHA" \
60 | --build-arg GITHUB_SHA="$GITHUB_SHA" \
61 | --build-arg GITHUB_REF="$GITHUB_REF" \
62 | .
63 |
64 | # Push the Docker image to Google Container Registry
65 | - name: Publish
66 | run: |-
67 | docker push "gcr.io/$PROJECT_ID/$IMAGE:$GITHUB_SHA"
68 |
69 | # Set up kustomize
70 | - name: Set up Kustomize
71 | run: |-
72 | curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v3.1.0/kustomize_3.1.0_linux_amd64
73 | chmod u+x ./kustomize
74 |
75 | # Deploy the Docker image to the GKE cluster
76 | - name: Deploy
77 | run: |-
78 | ./kustomize edit set image gcr.io/PROJECT_ID/IMAGE:TAG=gcr.io/$PROJECT_ID/$IMAGE:$GITHUB_SHA
79 | ./kustomize build . | kubectl apply -f -
80 | kubectl rollout status deployment/$DEPLOYMENT_NAME
81 | kubectl get services -o wide
82 |
--------------------------------------------------------------------------------
/src/Row.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | // imports DEFAULT(alias) export from axios.js
3 | import axios 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 | // Row component
11 | function Row({ title, fetchURL, isLargeRow }) {
12 | /* Creating a movie state (short term memory) */
13 | const [movies, setMovies] = useState([]);
14 | /* Creating a trailer state (short term memory) */
15 | const [trailerURL, setTrailerURL] = useState("");
16 | // Pulling information from tmdb API when the pages loads
17 | useEffect(() => {
18 | // Running async call
19 | async function fetchData() {
20 | // Waiting for the promise to come back with movie results, fetchURL(outside the code block)
21 | const request = await axios.get(fetchURL);
22 | setMovies(request.data.results);
23 | return request;
24 | }
25 | // if [empty], run once when the row loads, and dont run again
26 | fetchData();
27 | }, [fetchURL]);
28 | // console.log(movies);
29 |
30 | const opts = {
31 | height: "390",
32 | width: "100%",
33 | playerVars: {
34 | // https://developers.google.com/youtube/player_parameters
35 | autoplay: 1,
36 | },
37 | };
38 |
39 | // When user clicks on the movie picture
40 | const handleClick = (movie) => {
41 | // If trailer is found clear the url
42 | if (trailerURL) {
43 | setTrailerURL("");
44 | } else {
45 | // Search for movie trailer full url
46 | movieTrailer(movie?.name || "")
47 | .then((url) => {
48 | // https://www.youtube.com/watch?v=aSØDÆømlsdæ
49 | const urlParams = new URLSearchParams(new URL(url).search); // urlParams gives us everthing after the ?
50 | setTrailerURL(urlParams.get("v")); //urlParams gives us everything after v=
51 | // Displays error message if unable to find url
52 | })
53 | .catch((error) => console.log(error));
54 | }
55 | };
56 |
57 | return (
58 |
59 |
{title}
60 | {/* Container for movie rows */}
61 |
62 | {/* several row poster */}
63 | {/* Looping through movies array API */}
64 | {movies.map((movie) => (
65 | // returns movie images in new array
66 | handleClick(movie)}
71 | // All poster same size (row__poster) except if you are larger row, then use
72 | // isLargeRow
73 | className={`row__poster ${isLargeRow && "row__posterLarge"}`}
74 | // Loads poster images from base url
75 | src={`${base_URL}${
76 | isLargeRow ? movie.poster_path : movie.backdrop_path
77 | }`}
78 | alt={movie.name}
79 | />
80 | ))}
81 | {/* Contain -> posters */}
82 |
83 | {/* Embedding youtube movie trailers to show */}
84 | {trailerURL && }
85 |
86 | );
87 | }
88 |
89 | // Exporting Row function. Making it available
90 | export default Row;
91 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl, {
104 | headers: { 'Service-Worker': 'script' },
105 | })
106 | .then(response => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get('content-type');
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf('javascript') === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then(registration => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | 'No internet connection found. App is running in offline mode.'
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ('serviceWorker' in navigator) {
133 | navigator.serviceWorker.ready
134 | .then(registration => {
135 | registration.unregister();
136 | })
137 | .catch(error => {
138 | console.error(error.message);
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------