diff --git a/client/package-lock.json b/client/package-lock.json index ff54a88..02eb031 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -21,6 +21,7 @@ "react-scripts": "4.0.3", "react-slick": "^0.30.2", "react-toastify": "^8.1.0", + "react-youtube": "^10.1.0", "simple-peer": "^9.11.1", "slick-carousel": "^1.8.1", "socket.io-client": "^2.5.0", @@ -14311,6 +14312,11 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==" + }, "node_modules/loader-runner": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", @@ -17974,6 +17980,22 @@ "react-dom": ">=16" } }, + "node_modules/react-youtube": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-10.1.0.tgz", + "integrity": "sha512-ZfGtcVpk0SSZtWCSTYOQKhfx5/1cfyEW1JN/mugGNfAxT3rmVJeMbGpA9+e78yG21ls5nc/5uZJETE3cm3knBg==", + "dependencies": { + "fast-deep-equal": "3.1.3", + "prop-types": "15.8.1", + "youtube-player": "5.5.2" + }, + "engines": { + "node": ">= 14.x" + }, + "peerDependencies": { + "react": ">=0.14.1" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -19448,6 +19470,11 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "node_modules/sister": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sister/-/sister-3.0.2.tgz", + "integrity": "sha512-p19rtTs+NksBRKW9qn0UhZ8/TUI9BPw9lmtHny+Y3TinWlOa9jWh9xB0AtPSdmOy49NJJJSSe0Ey4C7h0TrcYA==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -23696,6 +23723,29 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/youtube-player": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/youtube-player/-/youtube-player-5.5.2.tgz", + "integrity": "sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==", + "dependencies": { + "debug": "^2.6.6", + "load-script": "^1.0.0", + "sister": "^3.0.0" + } + }, + "node_modules/youtube-player/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/youtube-player/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } }, "dependencies": { @@ -34380,6 +34430,11 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==" + }, "loader-runner": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", @@ -37318,6 +37373,16 @@ "clsx": "^1.1.1" } }, + "react-youtube": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-10.1.0.tgz", + "integrity": "sha512-ZfGtcVpk0SSZtWCSTYOQKhfx5/1cfyEW1JN/mugGNfAxT3rmVJeMbGpA9+e78yG21ls5nc/5uZJETE3cm3knBg==", + "requires": { + "fast-deep-equal": "3.1.3", + "prop-types": "15.8.1", + "youtube-player": "5.5.2" + } + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -38455,6 +38520,11 @@ } } }, + "sister": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sister/-/sister-3.0.2.tgz", + "integrity": "sha512-p19rtTs+NksBRKW9qn0UhZ8/TUI9BPw9lmtHny+Y3TinWlOa9jWh9xB0AtPSdmOy49NJJJSSe0Ey4C7h0TrcYA==" + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -41868,6 +41938,31 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "youtube-player": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/youtube-player/-/youtube-player-5.5.2.tgz", + "integrity": "sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==", + "requires": { + "debug": "^2.6.6", + "load-script": "^1.0.0", + "sister": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } } } } diff --git a/client/package.json b/client/package.json index 3826519..38b1cd6 100644 --- a/client/package.json +++ b/client/package.json @@ -16,6 +16,7 @@ "react-scripts": "4.0.3", "react-slick": "^0.30.2", "react-toastify": "^8.1.0", + "react-youtube": "^10.1.0", "simple-peer": "^9.11.1", "slick-carousel": "^1.8.1", "socket.io-client": "^2.5.0", diff --git a/client/src/App.js b/client/src/App.js index 98dbe5c..cb1ce4c 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useRef } from "react"; import Peer from "simple-peer"; import io from "socket.io-client"; import Form from "./components/Form"; @@ -9,6 +9,8 @@ import { FaMasksTheater, FaMicrophoneLines } from "react-icons/fa6"; import { LiaLaptopSolid } from "react-icons/lia"; import { ToastContainer, toast, Slide } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; +import { MediaRecorder, register } from "extendable-media-recorder"; +import { connect } from "extendable-media-recorder-wav-encoder"; // const socket = io.connect('http://localhost:5000/'); var socket = io("http://localhost:5000/"); @@ -23,6 +25,65 @@ function App() { const [peerConnection, setPeerConnection] = useState(); const [serverEngaged, setServerEngaged] = useState(false); + const streamRef = useRef(stream); + // const serverEngagedRef = useRef(serverEngaged); + const peerConnectionRef = useRef(peerConnection); + + async function record1() { + const mediaDevice = + audioInput == "device" + ? navigator.mediaDevices.getDisplayMedia.bind(navigator.mediaDevices) + : navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices); + + await register(await connect()); + + const stream = await mediaDevice({ audio: true }); + const audioTracks = stream.getAudioTracks(); + const audioStream = new MediaStream(audioTracks); + + for (const track of stream.getVideoTracks()) { + track.stop(); + } + + const mediaRecorder = new MediaRecorder(audioStream, { + mimeType: "audio/wav", + }); + + const chunks = []; + mediaRecorder.ondataavailable = function (e) { + chunks.push(e.data); + }; + + mediaRecorder.addEventListener("stop", () => { + const blob = new Blob(chunks, { type: "audio/wav" }); + const reader = new FileReader(); + reader.readAsArrayBuffer(blob); + + reader.onload = (event) => { + const arrayBuffer = event.target.result; + + var binary = ""; + var bytes = new Uint8Array(arrayBuffer); + var len = bytes.byteLength; + for (var i = 0; i < len; i++) { + binary += String.fromCharCode(bytes[i]); + } + + // Convert byte array to base64 + const base64data = btoa(binary); + + socket.emit("blob", base64data); + }; + }); + + mediaRecorder.start(); + + // Stop recording after 15 seconds + setTimeout(function () { + mediaRecorder.stop(); + }, 15000); + } + function record() { const mediaDevice = audioInput == "device" @@ -90,9 +151,10 @@ function App() { } function cleanUp() { - if (stream != null) { + const currentStream = streamRef.current; + if (currentStream) { console.log("Cleaning tracks"); - stream.getTracks().forEach((track) => track.stop()); + currentStream.getTracks().forEach((track) => track.stop()); } setStream(null); setisListening(false); @@ -108,7 +170,7 @@ function App() { // Handle peer events: peer.on("signal", (offerData) => { - console.log("Setting Offer!"); + console.log("Offer generated"); setOffer(JSON.stringify(offerData)); setPeerConnection(peer); }); @@ -124,14 +186,19 @@ function App() { }); } + useEffect(() => { + streamRef.current = stream; + peerConnectionRef.current = peerConnection; + }, [stream, peerConnection]); + useEffect(() => { if (offer) { - console.log("Offer updated:", offer); + console.log("Sending Offer"); let offerEncoded = btoa(offer); socket.emit("engage", offerEncoded); socket.on("serverEngaged", (answer) => { - console.log("ServerSDP: ", answer); + console.log("Received answer"); let decodedAnswer = atob(answer); if (!serverEngaged && !stream && !peerConnection.destroyed) { peerConnection.signal(decodedAnswer); @@ -145,6 +212,30 @@ function App() { useEffect(() => { socket.on("connect", () => { createPeerConnection(); + socket.emit("totalSongs", ""); + }); + + // socket.on("serverEngaged", (answer) => { + // console.log("Received answer"); + + // let decodedAnswer = atob(answer); + + // if ( + // !serverEngagedRef.current && + // !streamRef.current && + // !peerConnectionRef.current.destroyed + // ) { + // console.log("Adding answer"); + // peerConnectionRef.current.signal(decodedAnswer); + // } + + // console.log("Engaged Server"); + // setServerEngaged(true); + // }); + + socket.on("failedToEngage", () => { + console.log("Server failed to engage"); + stopListening(); }); socket.on("matches", (matches) => { @@ -153,6 +244,7 @@ function App() { setMatches(matches); console.log("Matches: ", matches); } else { + toast("No song found."); console.log("No Matches"); } @@ -189,7 +281,7 @@ function App() { function stopListening() { console.log("Pause Clicked"); cleanUp(); - peerConnection.destroy(); + peerConnectionRef.current.destroy(); setTimeout(() => { createPeerConnection(); @@ -209,7 +301,6 @@ function App() { setisListening(true); setStream(stream); - stream.getVideoTracks()[0].onended = stopListening; stream.getAudioTracks()[0].onended = stopListening; }) .catch((error) => { @@ -243,7 +334,7 @@ function App() { diff --git a/client/src/components/CarouselSliders.js b/client/src/components/CarouselSliders.js index 3275de9..9a4ac67 100644 --- a/client/src/components/CarouselSliders.js +++ b/client/src/components/CarouselSliders.js @@ -1,8 +1,33 @@ -import React from "react"; +import React, { useRef } from "react"; +import YouTube from "react-youtube"; import styles from "./styles/CarouselSliders.module.css"; const CarouselSliders = (props) => { const [activeIdx, setActiveIdx] = React.useState(0); + const players = useRef({}); + + const opts = { + // width: "420", + // height: "210", + }; + + const onReady = (event, videoId) => { + players.current[videoId] = event.target; + }; + + const onPlay = (event) => { + const videoId = event.target.getVideoData().video_id; + // Pause other videos + Object.values(players.current).forEach((player) => { + const otherVideoId = player.getVideoData().video_id; + if ( + otherVideoId !== videoId && + player.getPlayerState() === 1 /* Playing */ + ) { + player.pauseVideo(); + } + }); + }; return ( <> @@ -15,22 +40,22 @@ const CarouselSliders = (props) => { parseInt(h, 10) * 360 + parseInt(m, 10) * 60 + parseInt(s, 10); return ( - <> -
- -
- +
+ onReady(event, match.youtubeid)} + onPlay={onPlay} + /> +
); })} diff --git a/client/src/components/Form.js b/client/src/components/Form.js index c910124..014ab52 100644 --- a/client/src/components/Form.js +++ b/client/src/components/Form.js @@ -40,7 +40,7 @@ const Form = ({ socket }) => { return (
-
Download songs to the server
+
Add songs to the server