Video compression from client side before uploading - javascript

I want to get a compressed video before uploading it to the server in react. I tried bellow method but it does not work. In the code bellow I am getting a delayed stream and also if I get stream than also it is not working. Also I am not getting event.data at media recorder onloaddata.
import React, { useState, useRef, useEffect } from 'react'
function Test2() {
const [file, setFile] = useState(null)
const [compressedFile, setCompressedFile] = useState(null)
const [mediaStream, setMediaStream] = useState(null)
const videoRef = useRef(null)
const canvasRef = useRef(null)
const handleFileSelection = (event) => {
setFile(event.target.files[0])
}
const handleCompression = async () => {
const videoUrl = URL.createObjectURL(file)
const video = document.createElement('video')
video.src = videoUrl
video.onloadedmetadata = new Promise( () => {
console.log('first')
console.log(video)
const stream = video.captureStream(25)
setMediaStream(stream)
console.log('stream', stream)
})
console.log('00', mediaStream)
let temp = mediaStream
console.log('temp',temp)
const mediaRecorder = new MediaRecorder(temp)
console.log('00', mediaRecorder)
const chunks = []
mediaRecorder.ondataavailable = new Promise((event) => {
chunks.push(event.data)
console.log('gggeve',event.data)
console.log('chulk',chunks)
})
mediaRecorder.start()
await new Promise((resolve) => (mediaRecorder.onstop = resolve))
const recordedBlob = new Blob(chunks, { type: 'video/mp4' })
setCompressedFile(URL.createObjectURL(recordedBlob))
console.log('ff::', chunks)
}
return (
<>
<input type="file" onChange={handleFileSelection} />
<button onClick={handleCompression}>Compress</button>
<video ref={videoRef} src={file} autoPlay loop />
{console.log('0', compressedFile)}
{compressedFile && <a href={compressedFile}>Link to Compressed Video</a>}
</>
)
}
export default Test2

Related

My program cannot send video with chunks, it is sending as a one video, I wanna send it via chunks

I am struggling with the application, which should send video with chunks, hovewer I cannot send the file with chunks. I have this code for front end.
import React, { useState } from "react";
import axios from "axios";
function App() {
const [selectedFile, setSelectedFile] = useState();
const [isFilePicked, setIsFilePicked] = useState(false);
const [chunkSize, setChunkSize] = useState(null);
const [chunkNumber, setChunkNumber] = useState(null);
const [currentChunkIndex, setCurrentChunkIndex] = useState(null);
const [chumkedNumber, setChunkedNumber] = useState(null)
const GettingVideo = (event) => {
const Selected = event.target.files[0];
setIsFilePicked(true);
setSelectedFile(Selected);
setChunkSize(Math.floor(Selected.size))
setChunkNumber(Math.floor(Selected.size / 1000000))
setCurrentChunkIndex(Selected.size / (Math.floor(Selected.size / 1400000)))
}
const SubmitVideo = () => {
console.log(chunkNumber);
const formData = new FormData();
formData.append("flowChunkNumber", 1);
formData.append("flowChunkSize", chunkSize);
formData.append("flowTotalSize", chunkSize)
formData.append("flowCurrentChunkSize", currentChunkIndex)
formData.append("flowIdentifier", currentChunkIndex+"-selectedFile.name")
formData.append("flowFilename", selectedFile.name);
formData.append("flowRelativePath", selectedFile.name)
formData.append("flowTotalChunks", chunkNumber)
formData.append("file", selectedFile)
var config = {
method: 'post',
url: 'http://localhost:3000/upload',
headers: {
'Content-Type': 'Access-Control-Allow-Origin'
},
data: formData
}
axios(config)
.then((res) => {
console.log(res.data)
}).catch(err => console.log(err, "im error"))
}
return (
<div className="App">
<input type="file" name="file" onChange={GettingVideo} />
<div>
<button onClick={SubmitVideo}>Submit</button>
</div>
</div>
);
}
the question is how to send my file via chunks, as it is sending just for 1 chunk. this is the lib that I am using for backend ``
package

How to update the mute property of DOM Audio realtime

I need some help on how I can update the DOM audio in real time.
I'm trying to make the audio muted and unmuted based on the user preference,
The recording is saved in the public folder.
The audio is working fine on the first load if you set mute to be true or false, then when you try to change it, the mute props do not update.
Here is a short sample of my code, I'm using next.js.
import { RECORDING } from '#/constants/media.constant';
import { useEffect, useState } from 'react';
function Sample() {
const [mute, setMute] = useState(false);
useEffect(() => {
(() => playWithAudio())();
}, []);
const playWithAudio = () => {
const tryToPlay = setInterval(() => {
// audio recording, format mp3
const audio = new Audio(RECORDING.HELLO);
audio.loop = true;
audio.muted = mute;
audio
.play()
.then(() => {
clearInterval(tryToPlay);
})
.catch(() => console.info('User has not interacted with DOM yet.'));
}, 1000);
};
return (
<>
<button onClick={() => setMute(!mute)}>Click</button>
</>
);
}
export default Sample;
Any help would be appreciated.
Give this a try
import { useEffect, useState } from "react";
import { RECORDING } from "#/constants/media.constant";
function Sample() {
const [mute, setMute] = useState(false);
const [audio, setAudio] = useState(null);
useEffect(() => {
setAudio(new Audio(RECORDING.HELLO));
}, []);
useEffect(() => {
if (audio) playWithAudio();
}, [audio]);
const playWithAudio = () => {
const tryToPlay = setInterval(() => {
// audio recording, format mp3
audio.loop = true;
audio.muted = mute;
audio
.play()
.then(() => {
clearInterval(tryToPlay);
})
.catch(() => console.info("User has not interacted with DOM yet."));
}, 1000);
};
return (
<>
<button
onClick={() => {
if (audio) audio.muted = !mute;
setMute(!mute);
}}
>
{mute ? "UnMute" : "Mute"}
</button>
</>
);
}
export default Sample;

How to combine these two functions so one waits until it's finished and then runs the other

The two functions in question:
const handleSubmit = async (e) => {
e.preventDefault();
console.log(songLink)
const newSong = {
songName,
songLink,
userId
};
const song = await dispatch(postSong(newSong))
.catch(async (res) => {
const data = await res.json()
if (data && data.errors) setErrors(data.errors)
})
reset();
};
const uploadSong = (files) => {
console.log(files[0])
const formData = new FormData()
formData.append('file', songSelected)
formData.append('upload_preset', 'd3gthd7l')
Axios.post("https://api.cloudinary.com/v1_1/dyhfkvy6u/video/upload", formData).then((response) => {
console.log(response.data.url, 'responseeee')
setSongLink(response.data.url)
})
}
I need the uploadSong function to finish the upload so I can get the response.data.url and save it to a variable, THEN handle the submit and add the variable to my database when creating the song. I'm not sure if it's something small or if I'm completely missing a concept. Should I return the url and then await the function?
The entire file:
import { useState } from "react";
import { useDispatch } from "react-redux";
import { postSong } from "../../store/song";
import { useSelector } from "react-redux";
// import { Image, Audio } from 'cloudinary-react'
import Axios from 'axios'
const SongForm = () => {
const dispatch = useDispatch();
const [songName, setSongName] = useState("");
const [songLink, setSongLink] = useState("");
const [errors, setErrors] = useState([]);
const [songSelected, setSongSelected] = useState("")
const [url, setUrl] = useState('')
const reset = () => {
setSongName("");
setSongLink("");
// setAlbumName('');
// setArtistName('')
};
const user = useSelector((state) => state.session.user);
const userId = user?.id
const handleSubmit = async (e) => {
e.preventDefault();
console.log(songLink)
const newSong = {
songName,
songLink,
userId
};
const song = await dispatch(postSong(newSong))
.catch(async (res) => {
const data = await res.json()
if (data && data.errors) setErrors(data.errors)
})
reset();
};
const uploadSong = (files) => {
console.log(files[0])
const formData = new FormData()
formData.append('file', songSelected)
formData.append('upload_preset', 'd3gthd7l')
Axios.post("https://api.cloudinary.com/v1_1/dyhfkvy6u/video/upload", formData).then((response) => {
console.log(response.data.url, 'responseeee')
setSongLink(response.data.url)
})
}
return (
<div className="inputBox">
<h1>Add A Song</h1>
<ul>
{errors.map((error, idx) => <li className='errors' key={idx}>{error}</li>)}
</ul>
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setSongName(e.target.value)}
value={songName}
placeholder="Song Name"
name="Song Name"
/>
{/* <input type="text"
type="text"
onChange={(e) => setSongLink(e.target.value)}
value={songLink}
/> */}
<input
// type="text"
// onChange={(e) => setSongLink(e.target.value)}
type='file'
onChange={(e) => { setSongSelected(e.target.files[0]) }}
// value={songLink}
placeholder="Song Link"
name="Audio File"
/>
<button onClick={uploadSong} type="submit">Submit</button>
{/* <Audio cloudName='dyhfkvy6u' publicId='https://res.cloudinary.com/dyhfkvy6u/image/upload/v1639007386/x8cgeebtzdfeou4p6bhw.png' /> */}
</form>
</div>
);
};
export default SongForm;
It looks like you have a onSubmit handler for your form, but you are also assigning an onClick action for the form's submit button (with a button of type submit).
Namely:
<form onSubmit={handleSubmit}>
<button onClick={uploadSong} type="submit">Submit</button>
The impact of this is two actions will fire.
If I were you, I would remove the onClick (on the button) or the onSubmit (on the form) so that you only have one action that happens.
Then, let's say you decide to keep your onSubmit as the action you want to fire, in that function I would call the two functions you want to perform. If the first function (upload) is async, I'd await its result before calling the next function.

Show the previous content for 5 seconds even if the state updates?

I have a React application which uses a Django backend, I have used webSocket to connect with the backend which updates state when there are some changes. But the changes are very rapid, so only the last changes are visible. I want to show the previous message for a certain time before next message is displayed. Here is my code
import React, { useEffect, useState, useRef } from "react";
const Text = () => {
const [message, setMessage] = useState("");
const webSocket = useRef(null);
useEffect(() => {
webSocket.current = new WebSocket("ws://localhost:8000/ws/some_url/");
webSocket.current.onmessage = (res) => {
const data = JSON.parse(res.data);
setMessage(data.message);
};
return () => webSocket.current.close();
}, []);
return <p>{message}</p>;
};
export default Text;
So the message should be visible for certain time (in seconds, for eg - 5 seconds), then the next message should be shown. Any idea how that could be done?
const Text = () => {
const [messages, setMessages] = useState([]);
const currentMessage = messages[0] || "";
const [timer, setTimer] = useState(null);
// webSocket ref missing? ;-)
useEffect(() => {
webSocket.current = new WebSocket("ws://localhost:8000/ws/some_url/");
webSocket.current.onmessage = (res) => {
const data = JSON.parse(res.data);
setMessages((prevState) => [ ...prevState, data.message]);
};
return () => webSocket.current.close();
}, []);
// Remove the current message in 5 seconds.
useEffect(() => {
if (timer || !messages.length) return;
setTimer(setTimeout(() => {
setMessages((prevState) => prevState.slice(1));
setTimer(null);
}, 5000));
}, [messages, timer]);
return <p>{currentMessage}</p>;
};
You can create a custom hook to handle the message transition. Pass as argument the desired time you want to wait before showing the next message. You can use it in other parts of your code:
useQueu.js
const useQueu = time => {
const [current, setCurrent] = useState(null); //--> current message
const queu = useRef([]); //--> messages
useEffect(() => {
const timeout = setTimeout(() => {
setCurrent(queu.current.shift());
}, time);
return () => clearTimeout(timeout);
}, [current]);
const add = obj => {
if (!current) setCurrent(obj); //--> don't wait - render immediately
else {
queu.current.push(obj);
}
};
return [current, add];
};
Text.js
const Text = () => {
const [message, add] = useQue(5000);
const webSocket = useRef(null);
useEffect(() => {
webSocket.current = new WebSocket("ws://localhost:8000/ws/some_url/");
webSocket.current.onmessage = (res) => {
const data = JSON.parse(res.data);
add(data.message); //--> add new message
};
return () => webSocket.current.close();
}, []);
return <p>{message}</p>;
};
Working example

How to append html video tag to a div in React

import React,{useRef} from "react"
const FuncComp = () => {
const videoItems = useRef();
const addVideo (stream){
videoItems.current.append(<video src = {stream}> </video>);
};
return (
<div ref = {videoItems}>
</div>
)
}
But addVideo function is not working as i can not append video tag to the div. is there another way to achive this?
you can write your code like this:
import React, { useState } from "react";
import { v4 as uuidv4 } from "uuid";
const MyComponent = () => {
const [videos, setVideos] = useState([]);
const addVideo = (id, stream) => {
const videosTemp = [...videos];
videosTemp.push({ id, stream });
setVideos(videosTemp);
};
const onAddVideoClick = () => {
const id = uuidv4();
const stream = "https://www.youtube.com/watch?v=haMOUb3KVSo";
addVideo(id, stream);
};
return (
<div>
<button onClick={onAddVideoClick}>Add Video</button>
{videos.map((video) => {
<video key={video.id} src={video.src} />;
})}
</div>
);
};

Categories

Resources