I was trying to create an app that loads videos from an ARRAY on loop. I am using UseState to change the URI index after the video ends.
The player works perfectly for around 1-2 hours then it freezes the video and throws onError() which gives player error null and sometimes Player release time out.
I am thinking that it might be happening because of useState because it re-renders the component every time when index changes. Maybe this could be memory leakage or something else I am not sure.
please help me I seriously spent a week to find the solution. I even tried caching the videos using the filesystem. but still, it doesn't stop freezing.
const VideoPlayer = ({ wholeResult }) => {
const focuspoint = React.useRef(null);
const [index, setIndex] = React.useState(0);
const [progress, setProgress] = React.useState(false);
React.useEffect(() => {
if (wholeResult !== undefined) {
setProgress(true);
}
}, []);
const navigation = useNavigation();
return (
<View style={styles.container}>
<TouchableOpacity
onLongPress={() => {
navigation.navigate("Home");
}}
delayLongPress={3000}
>
{progress &&
wholeResult[index] !== "" &&
wholeResult[index] !== undefined
? <Video
ref={focuspoint}
style={styles.video}
source={{
uri: wholeResult.length == 1 ? wholeResult[0] : wholeResult[index],
}}
useNativeControls={false}
shouldPlay
resizeMode="stretch"
isLooping={wholeResult.length == 1 ? true : false}
onError={(error) =>
alert(error)
}
onPlaybackStatusUpdate={(status) =>
status?.didJustFinish == true
? setIndex((idx) => (idx == wholeResult.length - 1 ? 0 : idx + 1))
: null
}
/>
: null}
</TouchableOpacity>
</View>
);
};
export default VideoPlayer;
EDIT: I think this is the issue with expo-av only or it might be a bug with it. I tried with imageBackground and it's working is perfectly with the same pattern of code.
Answering my Own QuestionSo I couldn't find the solution to this problem but I got a trick to play, I used Webview to run Html containing a video looping function. But I have to use some parameters that could run in every device by enabling hardware acceleration. and it works for me.
<WebView
style={styles.container}
originWhitelist={["*"]}
allowFileAccess={true}
allowUniversalAccessFromFileURLs={true}
allowFileAccessFromFileURLs={true}
javaScriptEnabled={true}
domStorageEnabled={true}
allowsFullscreenVideo={true}
mixedContentMode='always'
androidLayerType="hardware"
androidHardwareAccelerationDisabled={false}
mediaPlaybackRequiresUserAction={false}
source={{
html: `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
box-sizing: border-box;
background: #000;
}
video {
width: 100vw;
height: 100vh;
object-fit: cover;
}
</style>
</head>
<body>
<video id="myVideo" autoplay muted></video>
<script>
var videoSource = new Array();
videoSource = ${JSON.stringify(vid)};
var videoCount = videoSource.length;
var elem = document.getElementById("myVideo");
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.mozRequestFullScreen) {
elem.mozRequestFullScreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen();
} else if (elem.msRequestFullscreen) {
elem.msRequestFullscreen();
}
document.getElementById("myVideo").setAttribute("src", videoSource[0]);
function videoPlay(videoNum) {
document
.getElementById("myVideo")
.setAttribute("src", videoSource[videoNum]);
document.getElementById("myVideo").load();
document.getElementById("myVideo").play();
}
document
.getElementById("myVideo")
.addEventListener("ended", myHandler, false);
var incr = (function () {
var i = 0;
return function () {
if (i > videoCount - 1) {
i = 0;
}
return i++;
};
})();
function myHandler() {
videoPlay(incr());
}
</script>
</body>
</html>
`,
}}
/>
Related
I tried to read read QR code thanks to javascript code found in this tutorial
The code provided by this tutorial works inside the codesandbox linked in the tutorial, however it doesn't work when I tired the same exact code on my laptop or on my remote webserver. I've litteraly copy and paste the code with the same file configuration, filenames ect... but I'm getting the following JS error on my browser :
SyntaxError: Identifier 'qrcode' has already been declared (at qrCodeScanner.js:1:1)
Since I run the exact same code I d'ont understand what is going on there. Is there something needed on the server side in order to make the code works that is not mentioned in the tutorial ?
If you want to see the code used and see it in action, you can teste the codesandbox instance there.
EDIT
Here's the code I use :
(HMTL)
<!DOCTYPE html>
<html>
<head>
<title>QR Code Scanner</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width; initial-scale=1.0, maximum-scale=1.0; user-scalable=0;" />
<link rel="stylesheet" href="./src/style.css" />
<script src="https://rawgit.com/sitepoint-editors/jsqrcode/master/src/qr_packed.js"></script>
</head>
<body>
<div id="container">
<h1>QR Code Scanner</h1>
<a id="btn-scan-qr">
<img src="https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2017/07/1499401426qr_icon.svg">
<a/>
<canvas hidden="" id="qr-canvas"></canvas>
<div id="qr-result" hidden="">
<b>Data:</b> <span id="outputData"></span>
</div>
</div>
<script src="./src/qrCodeScanner.js"></script>
</body>
</html>
(Javascript)
const qrcode = window.qrcode;
const video = document.createElement("video");
const canvasElement = document.getElementById("qr-canvas");
const canvas = canvasElement.getContext("2d");
const qrResult = document.getElementById("qr-result");
const outputData = document.getElementById("outputData");
const btnScanQR = document.getElementById("btn-scan-qr");
let scanning = false;
qrcode.callback = res => {
if (res) {
outputData.innerText = res;
scanning = false;
video.srcObject.getTracks().forEach(track => {
track.stop();
});
qrResult.hidden = false;
canvasElement.hidden = true;
btnScanQR.hidden = false;
}
};
btnScanQR.onclick = () => {
navigator.mediaDevices
.getUserMedia({ video: { facingMode: "environment" } })
.then(function(stream) {
scanning = true;
qrResult.hidden = true;
btnScanQR.hidden = true;
canvasElement.hidden = false;
video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
video.srcObject = stream;
video.play();
tick();
scan();
});
};
function tick() {
canvasElement.height = video.videoHeight;
canvasElement.width = video.videoWidth;
canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
scanning && requestAnimationFrame(tick);
}
function scan() {
try {
qrcode.decode();
} catch (e) {
setTimeout(scan, 300);
}
}
Problem
The problem is that you are probably using a live server or just opening the html file, but in the sandbox parcel-bundler is used. var qrcode from the library collides with your const qrcode.
Solutions
Type module
Replace
<script src="./src/qrCodeScanner.js"></script>
with
<script type="module" src="./src/qrCodeScanner.js"></script>
Rename
Change your variable to something else like const myQrcode
Use a bundler
You can use parcel-bundler as in the sandbox or any other that will resolve variable collision for you
I'm trying to separate characters based on what house they belong to in the API (http://hp-api.herokuapp.com/api/characters)
I have tried using .filter and .map, but have been unable to achieve that goal, I don't know if this is the right place to ask for help understanding how to achieve my goal.
Here is the code:
const studentArray = [];
async function getStudents(url) {
const student = await fetch(url);
const jsondata = await student.json();
jsondata.forEach((student) => {
studentArray.push(student);
});
}
getStudents("http://hp-api.herokuapp.com/api/characters/students").then(() => {
});
<!DOCTYPE html>
<html lang="en">
<head>
<script src="/testing/script.js"></script>
<link rel="stylesheet" href="/testing/styles.css" />
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<main>
<div id="name" class="container"><button onclick="Gryffindor">Test</button></div>
<div id="name" class="container"><button onclick="Slytherin">Test</button></div>
<div id="name" class="container"><button onclick="Ravenclaw">Test</button></div>
<div id="name" class="container"><button onclick="Hufflepuff">Test</button></div>
</main>
</body>
</html>
I've just had a look here and there seems to already be an endpoint set up to get characters from particular houses if that's what you want to do?
E.g. fetch(http://hp-api.herokuapp.com/api/characters/house/gryffindor) will return an array of students in Gryffindor.
You could refactor your getStudents function to take the house as an argument and make a GET request to the http://hp-api.herokuapp.com/api/characters/house/:house endpoint, using a template literal.
Also your onClick isn't invoking any function. I suggest you have a look here for an example of how to use onClick
One possible approach, and especially for the chosen API, is to fetch all student-data exactly once.
From this data one can create an own index of house-specific student-lists. For the chosen API one would realize that there are actually students listed with no relation/link into any house. And the API itself does not provide students which are not associated with a house.
Thus, the task which does render the house-based filter-items can take this additional information (no house) as much into account as the option of displaying any student regardless of the house (all students).
The next step would initialize the filter handling which is the rendering of student-items from the currently chosen (click event) house-specific student-list.
It uses the technique of event delegation. Thus, there is a single listener subscribed to a single element which is the root-node of the students filter (instead of subscribing the listener again and again to each filter-item).
function emptyElementNode(elmNode) {
[...elmNode.childNodes].forEach(node => node.remove());
}
function createFilterItem(houseName) {
houseName = houseName.trim();
const elmItem = document.createElement('li');
const elmButton = document.createElement('button');
elmItem.dataset.filterLabel = houseName;
elmButton.textContent =
(houseName === '') && 'No House' || houseName;
elmItem.appendChild(elmButton);
return elmItem;
}
function createStudentItem(studentData) {
const elmItem = document.createElement('li');
const elmImage = document.createElement('img');
elmImage.src = studentData.image;
elmImage.alt = elmImage.title = studentData.name;
elmItem.appendChild(elmImage);
return elmItem;
}
function renderStudentsFilter(houseBasedIndex) {
const houseNameList = Object.keys(houseBasedIndex);
// console.log({ houseNameList });
const filterRoot = document
.querySelector('[data-students-filter]');
if (filterRoot) {
const filterListRoot = houseNameList
.reduce((rootNode, houseName) => {
rootNode
.appendChild(
createFilterItem(houseName)
);
return rootNode;
}, document.createElement('ul'));
const allStudentsFilterItem =
createFilterItem('All Students');
allStudentsFilterItem.dataset.filterLabel = 'all-students';
filterListRoot.appendChild(allStudentsFilterItem);
emptyElementNode(filterRoot);
filterRoot.appendChild(filterListRoot);
return filterRoot;
}
}
function renderStudentItems(studentList) {
const displayRoot = document
.querySelector('[data-student-list]');
if (displayRoot) {
const listRoot = studentList
.reduce((rootNode, studentData) => {
rootNode
.appendChild(
createStudentItem(studentData)
);
return rootNode;
}, document.createElement('ul'));
emptyElementNode(displayRoot);
displayRoot.appendChild(listRoot);
}
}
function handleStudentsFilterFromBoundIndex({ target }) {
const filterItem = target.closest('li[data-filter-label]')
const { dataset: { filterLabel } } = filterItem;
// console.log({ filterItem, filterLabel });
const studentList = this[filterLabel];
renderStudentItems(studentList);
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
}
function initializeStudentsFilterHandling(filterRoot, houseBasedIndex) {
filterRoot
.addEventListener(
'click',
handleStudentsFilterFromBoundIndex.bind(houseBasedIndex)
);
}
function createStudentsFilterByHouse(studentList) {
const houseBasedIndex = studentList
.reduce((index, student) => {
const houseName =
student.house?.trim() ?? '';
(index[houseName] ??= []).push(student);
return index;
}, {});
// console.log({
// studentList,
// houseBasedIndex,
// });
const filterRoot = renderStudentsFilter(houseBasedIndex);
houseBasedIndex['all-students'] = studentList;
initializeStudentsFilterHandling(filterRoot, houseBasedIndex);
}
async function main() {
const url = 'http://hp-api.herokuapp.com/api/characters/students';
const response = await fetch(url);
const studentList = await response.json();
createStudentsFilterByHouse(studentList);
}
main();
body {
margin: 32px 0 0 0!important;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
ul li {
display: inline-block;
margin: 0 3px 2px 3px;
}
ul li img {
float: left;
height: 120px;
width: auto;
}
ul li img[src=""] {
height: 98px;
width: auto;
padding: 10px 5px;
border: 1px dashed #aaa;
}
[data-students-filter] {
position: fixed;
left: 0;
top: 0;
}
<article data-students-overview>
<navigation data-students-filter>
<em>+++ initialize house based students filter +++</em>
</navigation>
<section data-student-list>
</section>
</article>
I've a problem with a slider I've to do for school.
The console return no error, but the images don't show in the slider. I'm on it for four days now and I can't figure out what is the problem, so it seems that I need your lights ! ^^
I used the console to check if "diaporama.js" is working and it is, the console.log at the end of "slider.js" is to check if my image path is ok and it is. I've absolutely no clue of what is going wrong.
Thank you in advance !
Here is my code :
HTML
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta property="og:url" content="" />
<title>Slider</title>
<link rel="stylesheet" href="slide.css">
</head>
<body>
<h1>Test</h1>
<div id="caroussel">
<img src="" alt="diapo1" id="diapo">
<div id="precedent" ><</div>
<div id="suivant" >></div>
</div>
<script src="diaporama.js"></script>
<script src="slide.js"></script>
</body>
</html>
diaporama.js
class Diaporama {
constructor(src, images) {
this.src = src;
this.images = images;
this.position = 0;
this.start();
}
slideLeft() {
if (this.position <= 0) {
this.position = this.images.length - 1;
} else {
this.position--;
}
this.src = this.images[this.position];
}
slideRight() {
if (this.position > this.length-1) {
this.position = 0;
}
else {
this.position++;
}
this.src = this.images[this.position];
}
start() {
this.src = this.images[this.position];
}
}
slide.js
var images = Array('img/caroussel1.png', 'img/caroussel2.jpg', 'img/caroussel3.jpg', 'img/caroussel4.jpg', 'img/caroussel5.jpg');
var src = document.getElementById("diapo").src;
var diaporama = new Diaporama(src, images);
setInterval(function () { diaporama.slideLeft(); }, 5000);
console.log(src);
At a minimum, it looks like one problem is that:
You have an image element with ID diapo in your HTML, and then in the DOM, which has an empty src attribute.
In slide.js, you attempt to create a new instance of the class Diaporama, called diaporama, using the empty src attribute that you have stored in the variable src in slide.js.
Since an img element needs a src attribute with an actual URL, in order to show the image at that URL, you are seeing nothing, since you have not provided a URL (You won't get an error either, since an empty src attribute is perfectly valid HTML, and causes no JS errors)
UPDATE IN RESPONSE TO COMMENTS
The critical issue (or oversight), is that you have:
a carousel element in the index.html file, which is then represented in the DOM (which is what we expect)
an instance of class Diaporama called diaporama in slide.js, which has no link to the DOM carousel that you want it to have: all diaporama has is a String, taken from the src attribute of the DOM carousel, which refers to the URL path of various images. The diaporama instance can never "reach out" and update the DOM carousel, given the code you have written.
Thankfully, the fix is very simple.
As you know, there needs to be link between the DOM and object that you have created; creating such a link is straight-forward and just involves DOM queries.
I have added a solution (I have placed ALL the JS in one file, rather than two files -- as you have -- but this is not important)
class Diaporama {
constructor(imgElem, images) {
this.imgElem = imgElem;
this.images = images;
this.position = 0;
this.start();
}
slideLeft() {
if (this.position <= 0) {
this.position = this.images.length - 1;
} else {
this.position--;
}
// this is part of the 'bridge' between "carousel.js" and the DOM
this.imgElem.src = this.images[this.position];
}
slideRight() {
// there was an error in your original "slideRight" method: a typo and an "off-by-error"
if (this.position >= this.images.length-1) {
this.position = 0;
}
else {
this.position++;
}
// this is part of the 'bridge' between "carousel.js" and the DOM
this.imgElem.src = this.images[this.position];
}
start() {
// this is part of the 'bridge' between "carousel.js" and the DOM
this.imgElem.src = this.images[this.position];
}
}
// prefer an Array literal rather than call to Array -- less verbose, and slightly faster
var images = ['img/one.jpg', 'img/two.jpg', 'img/three.jpg'];
// This is where 'bridge' between "carousel.js" and the DOM is created: we 'cache' a reference to the carousel 'img' element,
// which we will then modify from within the 'carousel' instance of class Diaporama
var imgElem = window.document.getElementById('carousel').querySelector('img');
var carousel = new Diaporama(imgElem, images);
carousel.start();
// create 'delegated' event listener on document, and trigger correct method of 'carousel' in response to user interaction
window.document.addEventListener('click', ev => {
const target = ev.target;
if(target.id === 'back_button') {
carousel.slideLeft();
} else if(target.id === 'next_button') {
carousel.slideRight();
}
});
.carousel {
max-height: 400px;
max-width: 600px;
background: rgb(250,250,200);
overflow: hidden;
}
.button-wrapper {
display: flex;
justify-content: space-around;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Carousel</title>
</head>
<body>
<h1>Carousel slider (OOP)</h1>
<section id="carousel" class="carousel">
<div class="button-wrapper">
<button id="back_button">Back</button>
<button id="next_button">Next</button>
</div>
<img src="" alt="carousel image">
</section>
<script src="carousel.js"></script>
</body>
</html>
I hope this helps answer your question!
If it does, then please mark my answer as the accepted one.
If you still have questions, then let me know, and I will do my best to answer them.
I am trying to create a playlist with the code below but I seem to be getting some errors. Firebug is saying that the play() is not a function. please help I have spent half of my day trying to find a solution. Here is my code:
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script>
var current, playlist;
current = 0;
function() {
current++;
var songs = Array("en_ra1.mp3", "en_ra2.mp3", "en_ra3.mp3", "en_ra0.mp3");
playlist = songs.length;
if (current == playlist) {
//do nothing or stop
} else {
this.playOptions = document.getElementById("audio").src = songs[current];
this.playOptions.play();
}
}
</script>
</head>
<body>
<audio id="audio" onended="loadplaylist()" src="ny_option1.mp3" controls ></audio>
Note:when I include the autoplay attribute it's works just fine despite the error showing in firebug console.
I'm not seeing where you declare the loadplaylist function, presumably a typo
in your function you are setting this.playOptions to the string returned from the array, not the player, I think your function should read something like this:
function loadplaylist() {
current++;
var songs = Array("en_ra1.mp3", "en_ra2.mp3", "en_ra3.mp3", "en_ra0.mp3");
playlist = songs.length;
if (current == playlist) {
//do nothing or stop
} else {
this.playOptions = document.getElementById("audio");
this.playOptions.src = songs[current];
this.playOptions.play();
}
}
I need to play Google text-to-speech in JavaScript.
The idea is to use the web service:
http://translate.google.com/translate_tts?tl=en&q=This%20is%20just%20a%20test
And play it on a certian action, e.g. a button click.
But it seems that it is not like loading a normal wav/mp3 file:
<audio id="audiotag1" src="audio/example.wav" preload="auto"></audio>
<script type="text/javascript">
function play() {
document.getElementById('audiotag1').play();
}
</script>
How can I do this?
Another option now may be HTML5 text to speech, which is in Chrome 33+ and many others.
Here is a sample:
var msg = new SpeechSynthesisUtterance('Hello World');
window.speechSynthesis.speak(msg);
With this, perhaps you do not need to use a web service at all.
Here is the code snippet I found:
var audio = new Audio();
audio.src ='http://translate.google.com/translate_tts?ie=utf-8&tl=en&q=Hello%20World.';
audio.play();
You can use the SpeechSynthesisUtterance with a function like say:
function say(m) {
var msg = new SpeechSynthesisUtterance();
var voices = window.speechSynthesis.getVoices();
msg.voice = voices[10];
msg.voiceURI = "native";
msg.volume = 1;
msg.rate = 1;
msg.pitch = 0.8;
msg.text = m;
msg.lang = 'en-US';
speechSynthesis.speak(msg);
}
Then you only need to call say(msg) when using it.
Update: Look at Google's Developer Blog that is about Voice Driven Web Apps Introduction to the Web Speech API.
Very easy with responsive voice. Just include the js and voila!
<script src='https://code.responsivevoice.org/responsivevoice.js'></script>
<input onclick="responsiveVoice.speak('This is the text you want to speak');" type='button' value='🔊 Play' />
I don't know of Google voice, but using the javaScript speech SpeechSynthesisUtterance, you can add a click event to the element you are reference to. eg:
const listenBtn = document.getElementById('myvoice');
listenBtn.addEventListener('click', (e) => {
e.preventDefault();
const msg = new SpeechSynthesisUtterance(
"Hello, hope my code is helpful"
);
window.speechSynthesis.speak(msg);
});
<button type="button" id='myvoice'>Listen to me</button>
The below JavaScript code sends "text" to be spoken/converted to mp3 audio to google cloud text-to-speech API and gets mp3 audio content as response back.
var text-to-speech = function(state) {
const url = 'https://texttospeech.googleapis.com/v1beta1/text:synthesize?key=GOOGLE_API_KEY'
const data = {
'input':{
'text':'Android is a mobile operating system developed by Google, based on the Linux kernel and designed primarily for touchscreen mobile devices such as smartphones and tablets.'
},
'voice':{
'languageCode':'en-gb',
'name':'en-GB-Standard-A',
'ssmlGender':'FEMALE'
},
'audioConfig':{
'audioEncoding':'MP3'
}
};
const otherparam={
headers:{
"content-type":"application/json; charset=UTF-8"
},
body:JSON.stringify(data),
method:"POST"
};
fetch(url,otherparam)
.then(data=>{return data.json()})
.then(res=>{console.log(res.audioContent); })
.catch(error=>{console.log(error);state.onError(error)})
};
Run this code it will take input as audio(microphone) and convert into the text than audio play.
<!doctype HTML>
<head>
<title>MY Echo</title>
<script src="http://code.responsivevoice.org/responsivevoice.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.1/css/font-awesome.min.css" />
<style type="text/css">
body {
font-family: verdana;
}
#result {
height: 100px;
border: 1px solid #ccc;
padding: 10px;
box-shadow: 0 0 10px 0 #bbb;
margin-bottom: 30px;
font-size: 14px;
line-height: 25px;
}
button {
font-size: 20px;
position: relative;
left: 50%;
}
</style>
Speech to text converter in JS
var r = document.getElementById('result');
function startConverting() {
if ('webkitSpeechRecognition' in window) {
var speechRecognizer = new webkitSpeechRecognition();
speechRecognizer.continuous = true;
speechRecognizer.interimResults = true;
speechRecognizer.lang = 'en-IN';
speechRecognizer.start();
var finalTranscripts = '';
speechRecognizer.onresult = function(event) {
var interimTranscripts = '';
for (var i = event.resultIndex; i < event.results.length; i++) {
var transcript = event.results[i][0].transcript;
transcript.replace("\n", "<br>");
if (event.results[i].isFinal) {
finalTranscripts += transcript;
var speechresult = finalTranscripts;
console.log(speechresult);
if (speechresult) {
responsiveVoice.speak(speechresult, "UK English Female", {
pitch: 1
}, {
rate: 1
});
}
} else {
interimTranscripts += transcript;
}
}
r.innerHTML = finalTranscripts + '<span style="color:#999">' + interimTranscripts + '</span>';
};
speechRecognizer.onerror = function(event) {};
} else {
r.innerHTML = 'Your browser is not supported. If google chrome, please upgrade!';
}
}
</script>
</body>
</html>