From 1319573f9c0b56fd04515fdbac29e6d427023ac6 Mon Sep 17 00:00:00 2001 From: Vinod Bangera <vinodbangera@niveussolutions.com> Date: Tue, 30 Jan 2024 13:49:03 +0530 Subject: [PATCH] initial commit --- Dockerfile | 32 +++++++ README.md | 75 +++------------ nginx.conf | 18 ++++ package-lock.json | 136 +++++++++++++++++++++++++++ package.json | 1 + public/index.html | 6 +- src/App.css | 28 ++++++ src/App.js | 28 +++--- src/App.test.js | 8 -- src/Audio.js | 208 +++++++++++++++++++++++++++++++++++++++++ src/Loading.js | 39 ++++++++ src/News.js | 164 ++++++++++++++++++++++++++++++++ src/Styles.css | 148 +++++++++++++++++++++++++++++ src/Summary.js | 84 +++++++++++++++++ src/Video.js | 42 +++++++++ src/index.js | 14 +-- src/logo.svg | 1 - src/reportWebVitals.js | 13 --- src/setupTests.js | 5 - 19 files changed, 935 insertions(+), 115 deletions(-) create mode 100644 Dockerfile create mode 100644 nginx.conf delete mode 100644 src/App.test.js create mode 100644 src/Audio.js create mode 100644 src/Loading.js create mode 100644 src/News.js create mode 100644 src/Styles.css create mode 100644 src/Summary.js create mode 100644 src/Video.js delete mode 100644 src/logo.svg delete mode 100644 src/reportWebVitals.js delete mode 100644 src/setupTests.js diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d7bb48a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM node:18-alpine as react-build + +# Create app directory +WORKDIR /usr/src/app + +# Install app dependencies +COPY package*.json ./ + +# Bundle app source +COPY . . + +# Install dependencies +RUN yarn + +# Build +RUN yarn build + +# Environment +FROM nginx:alpine +COPY nginx.conf /etc/nginx/conf.d/configfile.template +COPY --from=react-build /usr/src/app/build /usr/share/nginx/html + +ENV PORT 3000 +ENV HOST 0.0.0.0 + + +# Expose port 3000 +EXPOSE 3000 + +# Run the app +# CMD [ "npm", "start" ] +CMD sh -c "envsubst '\$PORT' < /etc/nginx/conf.d/configfile.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" \ No newline at end of file diff --git a/README.md b/README.md index 58beeac..05dd80c 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,17 @@ -# Getting Started with Create React App +## to run -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). +`npm i` -## Available Scripts +`npm start` -In the project directory, you can run: +### To deploy: -### `npm start` +<!-- `sudo docker build -t gcr.io/tech-rnd-project/news-summary-fe . && sudo docker push gcr.io/tech-rnd-project/news-summary-fe && gcloud run deploy news-summary-fe --image gcr.io/tech-rnd-project/news-summary-fe --platform managed --allow-unauthenticated --port 3000 --region asia-south1` --> -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in your browser. +`gcloud builds submit --tag gcr.io/tech-rnd-project/news-summary-fe && gcloud run deploy news-summary-fe --image gcr.io/tech-rnd-project/news-summary-fe --platform managed --allow-unauthenticated --port 3000 --region asia-south1` -The page will reload when you make changes.\ -You may also see any lint errors in the console. - -### `npm test` - -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `npm run build` - -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. - -### `npm run eject` - -**Note: this is a one-way operation. Once you `eject`, you can't go back!** - -If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. - -You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). - -### Code Splitting - -This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) - -### Analyzing the Bundle Size - -This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) - -### Making a Progressive Web App - -This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) - -### Advanced Configuration - -This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) - -### Deployment - -This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) - -### `npm run build` fails to minify - -This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) +- This command consists of 3 commands: + 1. docker build + 2. docker push + 3. gcloud run deploy +- add the `--no-cache` tag to the docker build command before `-t`. That should solve any issues in building the images. diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..33bef44 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,18 @@ +server { + listen $PORT; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri /index.html; + } + + gzip on; + gzip_vary on; + gzip_min_length 10240; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml; + gzip_disable "MSIE [1-6]\."; + +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 619cd3d..06f528c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@testing-library/user-event": "^13.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-loader-spinner": "^5.4.5", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" } @@ -2270,6 +2271,29 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -5508,6 +5532,21 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/babel-plugin-styled-components": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", + "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "lodash": "^4.17.21", + "picomatch": "^2.3.1" + }, + "peerDependencies": { + "styled-components": ">= 2" + } + }, "node_modules/babel-plugin-transform-react-remove-prop-types": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", @@ -5826,6 +5865,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6261,6 +6308,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -6442,6 +6497,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -8904,6 +8969,19 @@ "he": "bin/he" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -14678,6 +14756,25 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-loader-spinner": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-5.4.5.tgz", + "integrity": "sha512-32f+sb/v2tnNfyvnCCOS4fpyVHsGXjSyNo6QLniHcaj1XjKLxx14L2z0h6szRugOL8IEJ+53GPwNAdbkDqmy4g==", + "dependencies": { + "react-is": "^18.2.0", + "styled-components": "^5.3.5", + "styled-tools": "^1.7.2" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-loader-spinner/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -15513,6 +15610,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -16009,6 +16111,40 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", + "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-is": ">= 16.8.0" + } + }, + "node_modules/styled-tools": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/styled-tools/-/styled-tools-1.7.2.tgz", + "integrity": "sha512-IjLxzM20RMwAsx8M1QoRlCG/Kmq8lKzCGyospjtSXt/BTIIcvgTonaxQAsKnBrsZNwhpHzO9ADx5te0h76ILVg==" + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", diff --git a/package.json b/package.json index 3a04027..9a06582 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@testing-library/user-event": "^13.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-loader-spinner": "^5.4.5", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, diff --git a/public/index.html b/public/index.html index aa069f2..b84a805 100644 --- a/public/index.html +++ b/public/index.html @@ -9,6 +9,10 @@ name="description" content="Web site created using create-react-app" /> + <!-- <meta + http-equiv="Content-Security-Policy" + content="upgrade-insecure-requests" + /> --> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <!-- manifest.json provides metadata used when your web app is installed on a @@ -24,7 +28,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - <title>React App</title> + <title>News Summary</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> diff --git a/src/App.css b/src/App.css index 74b5e05..68f99a1 100644 --- a/src/App.css +++ b/src/App.css @@ -1,5 +1,7 @@ .App { text-align: center; + /* background-color: rgb(255, 255, 255); */ + font-family: IBM Plex Sans, sans-serif; } .App-logo { @@ -36,3 +38,29 @@ transform: rotate(360deg); } } + +.header { + width: 98.8vw; + height: 5vh; + background-color: rgb(245, 241, 241); +} + +.heading { + /* color: #492e0a; */ + /* color: linear-gradient( + 45deg, + #405de6, + #5851db, + #833ab4, + #c13584, + #fd1d1d, + #f56040, + #f77737, + #fcaf45 + ); */ + + margin: 2.25rem; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); + font-size: 1.75rem; + /* font-family: "Your Custom Font", sans-serif; */ +} diff --git a/src/App.js b/src/App.js index 3784575..2e9860e 100644 --- a/src/App.js +++ b/src/App.js @@ -1,23 +1,19 @@ -import logo from './logo.svg'; -import './App.css'; +import "./App.css"; +import News from "./News"; +import Summary from "./Summary"; +import Video from "./Video"; function App() { return ( <div className="App"> - <header className="App-header"> - <img src={logo} className="App-logo" alt="logo" /> - <p> - Edit <code>src/App.js</code> and save to reload. - </p> - <a - className="App-link" - href="https://reactjs.org" - target="_blank" - rel="noopener noreferrer" - > - Learn React - </a> - </header> + {/* <div className="header"> */} + <h2 className="heading">News Summary</h2> + {/* <h2 className="heading">News Summary</h2> */} + {/* </div> */} + + <News /> + {/* <Summary /> */} + {/* <Video /> */} </div> ); } diff --git a/src/App.test.js b/src/App.test.js deleted file mode 100644 index 1f03afe..0000000 --- a/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(<App />); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/Audio.js b/src/Audio.js new file mode 100644 index 0000000..0b5c642 --- /dev/null +++ b/src/Audio.js @@ -0,0 +1,208 @@ +import React, { useEffect, useState } from "react"; +import "./Styles.css"; +import Video from "./Video"; +import Loading from "./Loading"; + +function Audio({ audioResponse, requestId }) { + const [videoResponse, setVideoResponse] = useState(""); + const [audioUrl, setAudioUrl] = useState(""); + const [loading, setLoading] = useState(""); + const [timer, setTimer] = useState(0); // Timer in seconds + + // console.log(requestId); + + const handleGetVideo = async () => { + if (audioResponse) { + setLoading(true); + setTimer(0); // Reset the timer + let audio = audioResponse; + let request_id = requestId; + // console.log(request_id); + try { + const response = await fetch("https://34.41.231.47:8000/get_video", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ audio, request_id }), // Send the summary as the request + }); + if (response.ok) { + const data = await response.json(); // Assuming the response is text + // console.log(data); + // console.log(data.video); + setVideoResponse(data.video); + // console.log(videoResponse); + } else { + console.error("Failed to fetch video"); + } + } catch (error) { + console.error("Error:", error); + } finally { + setLoading(false); + } + } + }; + + useEffect(() => { + if (audioResponse) { + try { + const audioDataUri = `data:audio/wav;base64,${audioResponse}`; + setAudioUrl(audioDataUri); + } catch (error) { + console.error("Error converting audio data to data URI:", error); + } + } + }, [audioResponse]); + + useEffect(() => { + console.log("Updated videoResponse:", videoResponse); + }, [videoResponse]); + + // const audioBlob = new Blob([audioResponse], { type: "audio/wav" }); + // const audioUrl = URL.createObjectURL(audioBlob); + + // console.log("Video: ", videoResponse); + // let x = audioUrl; + // console.log(audioUrl); + + useEffect(() => { + // Start the timer when loading is true + let interval; + if (loading) { + interval = setInterval(() => { + setTimer((prevTimer) => prevTimer + 1); + }, 1000); // Update the timer every 1 second + } else { + clearInterval(interval); // Clear the interval when loading is false + } + + return () => { + clearInterval(interval); // Cleanup the interval on unmount + }; + }, [loading]); + + function formatTime(seconds) { + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + const formattedMinutes = minutes < 10 ? `0${minutes}` : minutes; + const formattedSeconds = + remainingSeconds < 10 ? `0${remainingSeconds}` : remainingSeconds; + return `${formattedMinutes}:${formattedSeconds}`; + } + + // let videoResponse1 = + // "https://storage.googleapis.com/wav2lip-niveus/output/123/result.mp4"; + + const downloadVideo = () => { + if (videoResponse) { + // Downloads the file + const link = document.createElement("a"); + link.download = "News.mp4"; + const blob = new Blob([videoResponse], { type: "video/mp4" }); + console.log(blob); + link.href = URL.createObjectURL(blob); + link.click(); + URL.revokeObjectURL(link.href); + } + }; + + const handleTryNew = () => { + window.scrollTo({ + top: 0, + behavior: "smooth", // Use smooth scrolling behavior + }); + + // Reload the page after a short delay (e.g., 500 milliseconds) + setTimeout(() => { + window.location.reload(); + }, 600); + }; + + return ( + <div className="container"> + <div className="audio-container"> + <h2 className="sub-heading">Audio</h2> + + {audioUrl ? ( + <div> + <audio controls name="media"> + <source src={audioUrl} type="audio/wav" /> + </audio> + </div> + ) : ( + <div> + <audio controls name="media"> + {/* <source type="audio/wav" /> */} + </audio> + </div> + )} + {loading ? ( + <> + <button className="video-load-btn">Generating...</button> + <p className="get-video-msg"> + Please hold on!! Video generation will take around 2 to 3 minutes. + Timer: {formatTime(timer)} + </p> + {/* <p className="timer">Timer: {formatTime(timer)}</p> */} + </> + ) : ( + <button className="button" onClick={handleGetVideo}> + Get Video + </button> + )} + </div> + + {/* <Video videoResponse={videoResponse} requestId={requestId} /> */} + {/* Display the video response */} + <div className="video-container"> + <h2 className="sub-heading">Video</h2> + {/* <div className="video-container">{videoResponse}</div>{" "} */} + {loading ? ( + <div className="loading-container"> + <Loading /> + </div> + ) : ( + <> + <div> + <video + // width="320" + height="425" + controls + key={videoResponse} + > + <source src={videoResponse} type="video/mp4" /> + </video> + </div> + <div> + <button className="button" onClick={downloadVideo}> + Download + </button> + <button className="button" onClick={handleTryNew}> + Retry + </button> + </div> + </> + )} + {/* <div> + <video + // width="320" + height="425" + controls + key={videoResponse} + > + <source src={videoResponse} type="video/mp4" /> + </video> + </div> + <div> + <button className="button" onClick={downloadVideo}> + Download + </button> + </div> */} + {/* https://storage.googleapis.com/wav2lip-niveus/output/123/result.mp4 */} + {/* Display the video response */} + </div> + </div> + ); +} + +export default Audio; diff --git a/src/Loading.js b/src/Loading.js new file mode 100644 index 0000000..3a4db70 --- /dev/null +++ b/src/Loading.js @@ -0,0 +1,39 @@ +import { ThreeCircles, Dna, Bars } from "react-loader-spinner"; +import "./Styles.css"; + +const Loading = () => { + return ( + <div className="loader-container"> + {/* <ThreeCircles + height="70" + width="70" + color="grey" + wrapperStyle={{}} + wrapperClass="" + visible={true} + ariaLabel="three-circles-rotating" + outerCircleColor="" + innerCircleColor="" + middleCircleColor="" + /> */} + {/* <Dna + visible={true} + height="90" + width="90" + ariaLabel="dna-loading" + wrapperStyle={{}} + wrapperClass="dna-wrapper" + /> */} + <Bars + height="80" + width="80" + color="#005fa3" + ariaLabel="bars-loading" + wrapperStyle={{}} + wrapperClass="" + visible={true} + /> + </div> + ); +}; +export default Loading; diff --git a/src/News.js b/src/News.js new file mode 100644 index 0000000..af36a1a --- /dev/null +++ b/src/News.js @@ -0,0 +1,164 @@ +import { useState } from "react"; +import Summary from "./Summary"; + +function News() { + const [text, setText] = useState(""); + const [summary, setSummary] = useState(""); + const [loading, setLoading] = useState(false); + const [request_id, setRequestId] = useState(null); + + // let random_num = Math.floor(Math.random() * 1000); + // let request_id = random_num; + // let request_id = 123; + + function processNewsText(rawText) { + // Replace " with ' + let news = rawText.replace(/"/g, "'"); + + // Replace \n with space + news = news.replace(/\n/g, " "); + + // Replace \t with space + news = news.replace(/\t/g, " "); + + // Replace \r with space + news = news.replace(/\r/g, " "); + + // Replace ; with space + news = news.replace(/;/g, " "); + + // Replace # with space + news = news.replace(/#/g, " "); + + // Replace -- with space + news = news.replace(/--/g, " "); + + // Replace - with space + news = news.replace(/-/g, " "); + + // Replace () with space + news = news.replace(/\(/g, " "); + news = news.replace(/\)/g, " "); + + return news; + } + + const handleGetSummary = async () => { + if (text) { + setLoading(true); + // let request_id = 123; + let request_id = Math.floor(Math.random() * 1000); + setRequestId(request_id); + // console.log("Text>>>> ", text); + processNewsText = await processNewsText(text); + // console.log(processNewsText); + // console.log(request_id); + + // Set a flag to track if a response has been received + let responseReceived = false; + + // Set a timeout of 40 seconds (40,000 milliseconds) + const timeout = 90000; + + const timeoutId = setTimeout(() => { + if (!responseReceived) { + // Alert the user if no response has been received within the timeout + alert( + "Server is Busy processing another request. We have put you on queue!" + ); + setLoading(false); // Reset loading state + } + }, timeout); + + try { + const response = await fetch("https://34.41.231.47:8000/summarize", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ text: processNewsText, request_id }), + }); + if (response.ok) { + clearTimeout(timeoutId); // Clear the timeout if response is received + const data = await response.json(); + // console.log(data); + setSummary(data.summary); + responseReceived = true; + } else { + console.error("Failed to fetch summary"); + } + } catch (error) { + console.error("Error:", error); + } finally { + setLoading(false); + } + } else { + alert("Please enter news!!"); + } + }; + + // request_id = request_id; + // console.log(request_id); + + const fillWithSampleData = () => { + const sampleText = `New Delhi (India), August 29: Niveus Solutions, a cloud engineering organization, announced today that it has received the 2023 Google Cloud Partner of the Year Awards for two categories: Google Cloud Services Partner of the Year - Asia Pacific' and 'Google Cloud Expansion Partner of the Year - Asia Pacific'. This marks the second time that Niveus has been recognized by Google Cloud for their outstanding achievements. + + Niveus Solutions was recognized for its achievements in the Google Cloud ecosystem, helping customers across BFSI, Automotive, Media & Entertainment, Manufacturing, PSUs, and Digital Natives industries. Nievus is also recognized for its deep cloud transformation experience within the Asia Pacific region and its comprehensive and compelling digital solutions that are transforming the way industries work. + + "Google Cloud's partner awards recognize the significant impact and customer success that our partners have driven over the past year," said Kevin Ichhpurani, Corporate Vice President of global Ecosystem and Channels at Google Cloud. "We're delighted to recognize Niveus Solutions as a 2023 Google Cloud Partner Award winner, and look forward to a continued strong partnership in support of our mutual customers." + + The Google Cloud Service Partner of the Year - Asia Pacific award recognizes Niveus' commitment to enabling global innovators across diverse industries in their digital transformation. With exceptional technical capabilities and top-tier service offerings, Niveus is committed to delivering outstanding Google Cloud products and solutions as a Service Partner. Niveus' recognition as the Google Cloud Expansion Partner of the Year - Asia Pacific is a testament to its focus on driving growth and expansion throughout the region. Leveraging Google Cloud, Niveus facilitates the seamless adoption and implementation of cloud solutions, empowering businesses to scale rapidly and capitalize on new opportunities. + + "We're proud of the achievements we've made in the previous year and are excited to be named Google Cloud Services Partner of the Year - Asia Pacific and Google Cloud Expansion Partner of the Year - Asia Pacific. We are recognized for our strong partnership with Google Cloud and our best-in-class use of Google Cloud products and services, coupled with our expertise. Winning this prestigious award is a true testament to our agility, innovative leadership, exceptional customer service, and the remarkable growth that we have achieved," said Suyog Shetty, CEO of Niveus Solutions. + + In the previous year, Niveus achieved Infrastructure, Application development, and Data Management Specializations, acquiring 21 new areas of expertise and 167 Professional Google Cloud certifications. They were also recognized as a Google Cloud Regional AlloyDB partner. Notably, their collaboration on an automotive bookings platform resulted in record-breaking sales and improved customer satisfaction. Niveus expanded its team to over 900+ employees across 6+ locations and successfully entered the SEA market. They aim to replicate their success in India with Google in other international markets. Niveus strengthened its presence in the BFSI & DN space and expanded sales capacity to cover other industries like Manufacturing, CPG, and Conglomerates, successfully closing deals with various clients. + + Niveus Solutions Pvt. Ltd. is a cloud-born engineering services organization founded in 2013. Niveus progressed rapidly over the years with the company making a strategic decision in 2019 to exclusively partner with Google Cloud, securing 'Premier Partner status in less than 2 years. Niveus empowers enterprises to harness the power of cloud services and build resilient infrastructures that scale. Niveus specializes in application, infrastructure, & data modernization, data management, cloud consulting, security, & managed services. + `; + setText(sampleText); + }; + + // let request_id = request_id; + + return ( + <div className="container"> + <div className="news-container"> + <h2 className="sub-heading">News</h2> + <div className="sample-container"> + <label className="sample"> + <input + type="checkbox" + name="sampleData" + onClick={fillWithSampleData} + /> + Sample + </label> + </div> + <div className="textarea-container"> + <textarea + // rows="16" + // cols="120" + value={text} + onChange={(e) => setText(e.target.value)} + placeholder="Enter news text here..." + /> + </div> + {loading ? ( + <div> + <button className="generating-button">Generating...</button> + </div> + ) : ( + <div> + <button className="button" onClick={handleGetSummary}> + Get Summary + </button> + </div> + )} + </div> + + <Summary summary={summary} requestId={request_id} /> + </div> + ); +} + +export default News; diff --git a/src/Styles.css b/src/Styles.css new file mode 100644 index 0000000..45dcb9b --- /dev/null +++ b/src/Styles.css @@ -0,0 +1,148 @@ +/* Video.js */ +.video-container { + /* background-color: lightgrey; + min-height: 5rem; + min-width: 500px; + max-width: 50%; + padding: 1rem; */ +} + +.news-container { + margin: 2rem 8rem; + background-color: #eceef5; + /* border: 1px solid lightgray; */ + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); + /* box-shadow: rgba(0, 0, 0, 0.1) 0px 2px 4px; */ +} + +.summary-container { + margin: 2rem 8rem; + /* background-color: #faf5f5; */ + background-color: #eceef5; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +.audio-container { + margin: 2rem 8rem; + background-color: #eceef5; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +.video-container { + margin: 2rem 8rem; + background-color: #eceef5; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +.textarea-container { + /* padding: 1rem; */ +} + +textarea { + font-family: IBM Plex Sans, sans-serif; + color: #1f2937; + font-size: 0.85rem; + line-height: 1.1rem; + padding: 0.6rem; + /* margin: 1rem; */ + border: 1px solid #7d9dc5; + border-radius: 4px; + transition: background-color 0.5s ease; + width: 90%; + min-height: 15rem; +} + +textarea:hover { + border: 1px solid #20508a; +} + +/* Hide scrollbar for Webkit (Chrome, Safari) */ +textarea::-webkit-scrollbar { + display: none; +} + +.summary-texarea { + min-height: 7.5rem; +} + +.button { + padding: 0.8rem 2rem; + margin: 2rem; + border: none; + cursor: pointer; + /* background: #6c757d; */ + background: #08325f; + color: #fff; + border-radius: 5px; + font-size: 0.98; +} + +.button:hover { + background: #005fa3; +} + +.generating-button { + padding: 0.8rem 2rem; + margin: 2rem; + border: none; + cursor: pointer; + /* background: #6c757d; */ + background: #005fa3; + color: #fff; + border-radius: 5px; + font-size: 0.98; +} + +/* +h2 { + margin: 2rem; +} */ + +.sub-heading { + padding: 2rem; + margin-bottom: 0; + /* color: #412603; */ +} + +.video-load-btn { + padding: 0.8rem 2rem; + margin: 2rem 2rem 0; + border: none; + cursor: pointer; + background: #005fa3; + color: #fff; + border-radius: 5px; + font-size: 0.98; +} + +.get-video-msg { + font-size: 0.8rem; + color: rgb(179, 172, 172); + padding: 1rem; + padding-top: 0; +} + +.loader-container { + /* align-items: center; */ + min-height: 40vh; + margin-top: 7rem; +} + +.loading-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 100%; +} + +.sample-container { + text-align: right; +} + +.sample { + /* text-align: left; */ + font-size: 0.8rem; + margin-right: 4rem; + /* margin-bottom: 1rem; */ +} diff --git a/src/Summary.js b/src/Summary.js new file mode 100644 index 0000000..f9556a6 --- /dev/null +++ b/src/Summary.js @@ -0,0 +1,84 @@ +// Summary.js +import React, { useEffect, useState } from "react"; +import Video from "./Video"; +import Audio from "./Audio"; + +function Summary({ summary, requestId }) { + const [audioResponse, setAudioResponse] = useState(""); // State to store video response + // const [audioDisplay, setAudioDisplay] = useState(""); + const [loading, setLoading] = useState(false); + // console.log(requestId); + + const handleGetAudio = async () => { + if (summary) { + setLoading(true); + let request_id = requestId; + // console.log(summary); + // console.log(request_id); + let text = summary; + try { + const response = await fetch("https://34.41.231.47:8000/get_audio", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ text, request_id }), // Send the summary as the request + }); + if (response.ok) { + const data = await response.json(); // Assuming the response is text + // console.log(data); + // console.log(data.audio); + // const audioObjectUrl = URL.createObjectURL(data.audio); + // console.log(audioObjectUrl); + setAudioResponse(data.audio); + // setAudioDisplay() + // console.log(audioResponse); + } else { + console.error("Failed to fetch video"); + } + } catch (error) { + console.error("Error:", error); + } finally { + setLoading(false); + } + } + }; + + useEffect(() => { + // console.log("Updated audioResponse:", audioResponse); + }, [audioResponse]); + + return ( + <div className="container"> + <div className="summary-container"> + <h2 className="sub-heading">Summary</h2> + <div> + <textarea + className="summary-texarea" + // rows="6" + // cols="120" + value={summary} + readOnly + placeholder="Summary will display here..." + /> + </div> + {loading ? ( + <div> + <button className="generating-button">Generating...</button> + </div> + ) : ( + <div> + <button className="button" onClick={handleGetAudio}> + Get Audio + </button> + </div> + )} + </div> + + <Audio audioResponse={audioResponse} requestId={requestId} /> + {/* Pass videoResponse as prop */} + </div> + ); +} + +export default Summary; diff --git a/src/Video.js b/src/Video.js new file mode 100644 index 0000000..ff77317 --- /dev/null +++ b/src/Video.js @@ -0,0 +1,42 @@ +// Video.js +import React from "react"; +import "./Styles.css"; + +function Video({ videoResponse }) { + console.log(videoResponse); + // Function to handle the download button click + const handleDownloadClick = () => { + const a = document.createElement("a"); + a.href = videoResponse; + a.download = "video.mp4"; // Set the default download file name + a.style.display = "none"; // Hide the anchor element + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + }; + + return ( + <div className="video-container"> + <h2 className="sub-heading">Video</h2> + {/* <div className="video-container">{videoResponse}</div>{" "} */} + <div> + <video + // width="320" + height="425" + controls + key={videoResponse} + > + <source src={videoResponse} type="video/mp4" /> + </video> + </div> + <div> + <button className="button" onClick={handleDownloadClick}> + Download + </button> + </div> + {/* Display the video response */} + </div> + ); +} + +export default Video; diff --git a/src/index.js b/src/index.js index d563c0f..e56c4a3 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,10 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; +import React from "react"; +import ReactDOM from "react-dom/client"; +import "./index.css"; +import App from "./App"; +// import reportWebVitals from './reportWebVitals'; -const root = ReactDOM.createRoot(document.getElementById('root')); +const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <React.StrictMode> <App /> @@ -14,4 +14,4 @@ root.render( // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +// reportWebVitals(); diff --git a/src/logo.svg b/src/logo.svg deleted file mode 100644 index 9dfc1c0..0000000 --- a/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg> \ No newline at end of file diff --git a/src/reportWebVitals.js b/src/reportWebVitals.js deleted file mode 100644 index 5253d3a..0000000 --- a/src/reportWebVitals.js +++ /dev/null @@ -1,13 +0,0 @@ -const reportWebVitals = onPerfEntry => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/src/setupTests.js b/src/setupTests.js deleted file mode 100644 index 8f2609b..0000000 --- a/src/setupTests.js +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; -- GitLab