How to correctly play audio on a page in a browser (GridFS). Uploading audio to the database MongoDB is in progress.I have structure MERN server-> 1.controllers-> auth.js , meditation.js , mood.js , program.js , steps.js , test.js 2.data -> meditations.js , questsions.js 3.models -> Meditation.js , Combination.js , Mood.js , Levels.js , Qustsions.js , Result.js , Reward.js , SevedMeditation.js , Steps.js , User.js4.multer -> multer.js 5.routes -> auth.js , meditation.js , mood.js , steps.js , test.js 6.utils-> checkAuth.js ; client -> 1.components , 2.database , 3.hooks , 4.pages-> all pages for example meditation.jsx , main.jsx , mood.jsx , 5.redux-> store.js , authSlice.js , meditationSlice , mood_reducer , moodSlice , questsions_reducer , result_reducer , utils-> axios.js and App.js
//multer.js
import multer from 'multer';
import mongoose from 'mongoose';
import gridfs from 'gridfs-stream';
const connection = mongoose.connection;
gridfs.mongo = mongoose.mongo;
let gfs;
connection.once('open', () => {
gfs = gridfs(connection.db);
});
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
export { upload, gfs };
//controlers meditation.js
import meditation from "../data/meditation.js";
import User from "../models/User.js";
import Meditation from "../models/Meditation.js";
import SavedMeditation from "../models/SavedMeditation.js";
import mongoose from 'mongoose';
import { MongoClient, ObjectId } from 'mongodb';
import fs from 'fs';
import stream from 'stream';
import path from 'path';
const connectionUri = 'mongodb+srv://test:[email protected]/';
const dbName = 'test'; // Replace with your actual database name
import { streamFile } from '../multer/multer.js';
import { gfs } from '../multer/multer.js';
//get meditations from db
export const getMeditations= async (req, res) => {
try {
const m = await Meditation.find();
res.json(m)
} catch (error) {
res.json({ error })
}
}
//get One meditations from db
export const getOneMeditation= async (req, res) => {
try {
const meditation = await Meditation.findById(req.params.meditationId)
res.json(meditation)
} catch (error) {
res.json({ error })
}
}
// Dodanie medytacji
export const insertMeditations = async (req, res) => {
try {
await Meditation.insertMany( meditation );
res.json({ msg: "Data Saved Successfully...!" });
} catch (error) {
res.json({ error });
}
};
//Dodanie auio do medytacji
export const insertAudioToMeditations = async (req, res) => {
try {
const meditations = ["65525551782459e2a11e271a","65525551782459e2a11e271b",
"65525551782459e2a11e271c","65525551782459e2a11e271d",
"65525551782459e2a11e271e","65525551782459e2a11e271f",
"65525551782459e2a11e2720","65525551782459e2a11e2721"
];
const filePaths = ["D:\\study\\dyplom\\server\\data\\M1.MP3","D:\\study\\dyplom\\server\\data\\M2.MP3",
"D:\\study\\dyplom\\server\\data\\M3.MP3","D:\\study\\dyplom\\server\\data\\M4.MP3",
"D:\\study\\dyplom\\server\\data\\M5.MP3","D:\\study\\dyplom\\server\\data\\M5.MP3",
"D:\\study\\dyplom\\server\\data\\M5.MP3","D:\\study\\dyplom\\server\\data\\M5.MP3"
];
const client = new MongoClient(connectionUri, { useNewUrlParser: true, useUnifiedTopology: true });
await client.connect();
const database = client.db(dbName);
// Access the GridFS bucket
const bucket = new mongoose.mongo.GridFSBucket(database);
for (let i = 0; i < filePaths.length; i++) {
const filePath = filePaths[i];
const meditationId = meditations[i];
console.log(meditationId)
console.log(filePath)
// Read file content
const buffer = fs.readFileSync(filePath);
// Create a readable stream from the buffer
const bufferStream = new stream.PassThrough().end(buffer);
// Extract filename from the path
const filename = path.basename(filePath);
// Create a write stream to GridFS
const uploadStream = bucket.openUploadStream(filename);
bufferStream.pipe(uploadStream);
// Wait for the upload to complete
await new Promise((resolve, reject) => {
uploadStream.on('finish', resolve);
uploadStream.on('error', reject);
});
// Update the meditation with the new audio information
await Meditation.updateOne(
{ _id: meditationId },
{ $set: { audio: { file_id: uploadStream.id, filename } } }
);
}
res.json({ msg: "Audio Saved Successfully...!" });
} catch (error) {
res.json({ error });
}
};
//dodanie zapisanej medytacji do user
export const insertSavedMeditations = async (req, res) => {
try {
const user = await User.findById(req.body.userId);
const meditation = await Meditation.findById(req.body.meditationId);
//const {title, description} = req.body
if (!user) {
throw new Error('User not found Meditation not saved');
}
if (!meditation) {
throw new Error('Meditation not found Meditation not saved');
}
const newSavedMeditation = new SavedMeditation({
username: user.username,
title: meditation.title,
description: meditation.description,
meditationId: meditation._id,
userId: user._id,
})
await newSavedMeditation.save()
await User.findByIdAndUpdate(req.body.userId, {
$push: { savedMeditations: newSavedMeditation },
})
res.json({ msg: "Data Saved Successfully...!" });
console.log(newSavedMeditation);
} catch (error) {
res.json({ msg: 'Error w controllers' });
}
};
//Get user SavedMeditation
export const getMySavedMeditations = async (req, res) => {
try {
const user = await User.findById(req.query.userId);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
const list = await Promise.all(
user.savedMeditations.map((savedMeditation) => {
return SavedMeditation.findById(savedMeditation._id);
})
);
res.json(list);
} catch (error) {
res.status(500).json({ message: 'Something went wrong' });
}
};
//Delete SavedMeditation
export const removeSavedMeditation = async (req, res) => {
try {
const savedMeditation = await SavedMeditation.findByIdAndDelete(req.params.savedMeditationId)
console.log(`SavedMeditationId ${req.params.savedMeditationId}`)
console.log(`UserId ${req.query.id}`)
console.log(`REQ body ${req.query}`)
if (!savedMeditation) return res.json({ message: 'Takiej medytacji nie istnieje' })
//const user = await User.findById(req.query.userId);
await User.findByIdAndUpdate(req.query.id, {
$pull: { savedMeditations: req.params.savedMeditationId },
})
res.json({ message: 'Zapisana medytacja została usunięta.' })
} catch (error) {
res.json({ message: 'Coś poszło nie tak.' })
}
}
export const streamAudio = async (req, res) => {
try {
const fileId = req.params.fileId;
const audioStream = gfs.createReadStream({ _id: mongoose.Types.ObjectId(fileId) });
// Set appropriate headers
res.set('Content-Type', 'audio/mpeg');
// Pipe the audio stream to the response
audioStream.pipe(res);
} catch (error) {
res.status(500).json({ error: 'Error streaming audio' });
}
};
import { Router } from 'express'
import { checkAuth } from '../utils/checkAuth.js'
import { getMeditations, getMySavedMeditations, insertMeditations, insertSavedMeditations, removeSavedMeditation , insertAudioToMeditations, getOneMeditation , streamAudio} from '../controllers/meditation.js'
import {upload} from '../multer/multer.js'
const router = new Router()
//Get all meditations
router.get('/meditations',getMeditations)
//Get One meditation
router.get('/:meditationId',getOneMeditation)
// insert meditations
// http://localhost:3002/api/test
router.post('/meditations',insertMeditations)
//dodanie zapisanej medytacji do user
router.post('/',insertSavedMeditations)
//Dodanie auio do medytacji
router.post('/audioMeditationsSaved', upload.single('audio'),insertAudioToMeditations)
//Get user SavedMeditation
router.get('/',getMySavedMeditations)
//Delete SavedMeditation
router.delete('/:savedMeditationId',removeSavedMeditation)
export default router
//router.get('/stream/:meditationId/audio', streamAudio);
router.get('/stream/:fileId', streamAudio);
import React, { useState, useEffect, useRef } from 'react';
import { useDispatch , useSelector} from 'react-redux';
import styles from './styles.module.css';
import star from './images/star.png';
import Play from './images/Play.png';
import Stop from './images/Stop.png';
import axios from 'axios';
import Next from './images/Next.png';
import { getOneMeditation } from '../../redux/features/meditationSlice';
import { useParams } from 'react-router-dom';
function Meditation() {
const state = useSelector(state => state)
const {meditations, savedMeditation} = useSelector((state) => state.meditation);
const { meditationId } = useParams();
console.log(state);
console.log(meditationId);
let yourAudioFile;
let meditationOne ;
meditations.forEach((meditation, i) => {
// Użyj tutaj składni warunkowej
if (meditation._id === meditationId) {
// Tutaj użyj yourAudioFile
meditationOne = meditation;
yourAudioFile = meditation.audio.filename; //ЦЕ НАЗВА АУДИО ФАЙЛА!!!!!!!!!!
}
});
console.log(meditationOne);
console.log(yourAudioFile);
const numStars = 170;
const [cloudElements] = useState([]);
const audioRef = useRef(null);
const [isPlaying, setIsPlaying] = useState(false);
const [audioPosition, setAudioPosition] = useState(0);
const [starsAnimated, setStarsAnimated] = useState(true);
const [isAudioLoaded, setIsAudioLoaded] = useState(false);
const [isAudioEnded, setIsAudioEnded] = useState(false);
const [showNextButton, setShowNextButton] = useState(false);
const [audioDuration, setAudioDuration] = useState(0);
const [highlightAudio, setHighlightAudio] = useState(false);
const starElements = [];
for (let i = 0; i < numStars; i++) {
const starStyle = {
top: `${25 + Math.random() * 60}vh`,
left: `${15 + Math.random() * 80}vw`,
animationDelay: `${Math.random() * 5}s`,
animationPlayState: starsAnimated ? 'running' : 'paused', // Control animation state
};
starElements.push(
<img
key={`star-${i}`}
src={star}
alt={`Star ${i}`}
className={styles.star}
style={starStyle}
/>
);
}
useEffect(() => {
if (audioRef.current) {
if (isPlaying) {
audioRef.current.pause();
} else {
audioRef.current.currentTime = 0;
audioRef.current.play();
}
setIsPlaying(!isPlaying);
setStarsAnimated(!isPlaying);
}
}, [isPlaying, audioRef.current]);
useEffect(() => {
const audio = new Audio(yourAudioFile);
const handleCanPlayThrough = () => {
setIsAudioLoaded(true);
};
console.log(`AUDIO ${audio}`)
const handleEnded = () => {
setIsPlaying(false);
setStarsAnimated(true);
setIsAudioEnded(true); // Встанови стан, що аудіо відтворено до кінця
setShowNextButton(true); // Покажи кнопку "Далі" після завершення відтворення
};
audio.addEventListener('canplaythrough', handleCanPlayThrough);
audio.addEventListener('ended', handleEnded);
audioRef.current = audio;
return () => {
audio.removeEventListener('canplaythrough', handleCanPlayThrough);
audio.removeEventListener('ended', handleEnded);
};
}, [yourAudioFile]);
const handleTogglePlay = () => {
if (audioRef.current && isAudioLoaded) {
if (isPlaying) {
audioRef.current.pause();
} else {
audioRef.current.currentTime = 0;
audioRef.current.play();
}
setIsPlaying(!isPlaying);
setStarsAnimated(!isPlaying);
}
};
useEffect(() => {
const audio = new Audio();
const handleCanPlayThrough = () => {
setIsAudioLoaded(true);
};
const handleEnded = () => {
setIsPlaying(false);
setStarsAnimated(true);
setIsAudioEnded(true);
setShowNextButton(true);
};
audio.addEventListener('canplaythrough', handleCanPlayThrough);
audio.addEventListener('ended', handleEnded);
audioRef.current = audio;
return () => {
audio.removeEventListener('canplaythrough', handleCanPlayThrough);
audio.removeEventListener('ended', handleEnded);
};
}, [yourAudioFile]);
useEffect(() => {
if (audioRef.current) {
const handleLoadedData = () => {
setIsAudioLoaded(true);
};
audioRef.current.addEventListener('loadeddata', handleLoadedData);
return () => {
audioRef.current.removeEventListener('loadeddata', handleLoadedData);
};
}
}, [audioRef.current]);
const streamAudio = async (fileId) => {
try {
const response = await axios.get(`/api/meditations/stream/${fileId}`, {
responseType: 'arraybuffer',
});
const blob = new Blob([response.data], { type: 'audio/mpeg' });
const blobUrl = URL.createObjectURL(blob);
const audio = new Audio(blobUrl);
audio.addEventListener('loadeddata', () => {
audio.play();
setIsPlaying(true);
setStarsAnimated(false);
});
audio.addEventListener('ended', () => {
setIsPlaying(false);
setStarsAnimated(true);
setIsAudioEnded(true);
setShowNextButton(true);
});
audioRef.current = audio;
} catch (error) {
console.error('Error streaming audio:', error);
}
};
const fileId = meditationOne.audio.file_id;
return (
<div className={styles.container}>
{starElements}
{cloudElements}
<div className={styles.textOverlay}>
<h1>{meditationOne.title}</h1>
</div>
<button onClick={() => streamAudio(meditationOne.audio.file_id)}>Play</button>
</div>
);
}
export default Meditation;