playing s3 audio and downloading the file in react component - javascript

I am trying to build out a component that can play audio files from my s3 bucket and have a button that will download the file locally if desired.
Here is what I have right now based on my research but it does not work. I think I may need to download a library?
Code:
import React from 'react';
<script src="https://unpkg.com/boxicons#latest/dist/boxicons.js"></script>
function AudioFilePlayer(props) {
var name = props.name;
var id = props.id;
var s3_url = props.s3_url;
const handleAudioPlay = (s3_url_link, state) => {
var audio = new Audio(s3_url_link)
if (state) {
audio.play()
}
else {
audio.pause()
}
}
//no clue how to download
return (
<div className="AudioFileListItem">
<div className="AudioFileListElements">
<a href="javascript:void(0);" onClick={handleAudioPlay(s3_url, true)}>
<span className="AudioFilePlayButton">
<box-icon name='play-circle' color='#ffffff' size="sm"></box-icon>
</span>
<span className="AudioFilePlayButtonTitle">
Play
</span>
</a>
<a ref="javascript:void(0);" onClick={handleDownloadPlay(s3_url, true)}>
<span className="AudioFileDownloadButton">
<box-icon name='download' type='solid' color='#ffffff' size="sm"></box-icon>
</span>
<span className="AudioFileDownloadButtonTitle">
Download
</span>
</a>
</div>
</div>
);
}
export default AudioFileListItem;
I tested the aws s3_url and it works great. It downloads the correct wav file in my browser when the link is opened.
Example link would be:
https://bobmarley.s3.amazonaws.com/music/34/reggae.wav
My desired outcome is to be ablet o click the play html (button for user), have it change to the pause look (I can do this easily), and then actually play the audio. When I want to stop the audio, I can press it and it should pause and show the play button again.
For the download button, I just want to download the file. I know I can do this simply by opening the url in a tab but I don't want to reveal the URL. I want to know if theres another way to do it without showing the user the url.
I've been at this for weeks so truly any help would mean the world to me.
Thank you and this community so much!

I change 2 things and it's works fine from my side
insted of using onClick={handleAudioPlay(s3_url, true) use onClick={()=>handleAudioPlay(s3_url, true)}
since I could not access https://bobmarley.s3.amazonaws.com/music/34/reggae.wav from my side i use this url https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3 to test.
ReactDOM.render(<AudioFilePlayer />, document.getElementById("root"));
function AudioFilePlayer(props) {
let name = props.name;
let id = props.id;
let s3_url = "https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3";
const handleAudioPlay = (s3_url_link, state) => {
var audio = new Audio(s3_url_link);
if (state) {
audio.play();
} else {
audio.pause();
}
};
//no clue how to download
return (
<div className="AudioFileListItem">
<div className="AudioFileListElements">
<a
href="javascript:void(0);"
onClick={()=>handleAudioPlay(s3_url, true)}
>
<span className="AudioFilePlayButton">
<box-icon
name="play-circle"
color="#ffffff"
size="sm"
></box-icon>
</span>
<span className="AudioFilePlayButtonTitle">Play</span>
</a>
<a
href="javascript:void(0);"
// onClick={handleDownloadPlay(s3_url, true)}
>
<span className="AudioFileDownloadButton">
<box-icon
name="download"
type="solid"
color="#ffffff"
size="sm"
></box-icon>
</span>
<span className="AudioFileDownloadButtonTitle">
Download
</span>
</a>
</div>
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related

React refreshes component after function call

I'm trying to create a portfolio where to display all of my ML projects that can be displayed with a simple API call and I decided to create a WebApp. I'm using React for the frontend and Django for the backend. The goal is to make a single page that doesn't need to refresh or change the page.
I've pretty much done everything, but on testing the API for the first project, I notice that after the API's function call, the page refreshes and I can't seem to figure out why it does.
App Component:
class App extends Component {
constructor(props) {
super(props);
this.state = {
menu: 1,
file: null
};
}
fileSelect = event =>{
this.state.file = event.target.files[0];
}
update_menu_item(new_item){
this.setState({menu: new_item});
}
// HERE PROBLEM
uploadSelfie(){
const fd = new FormData();
fd.append('image', this.state.file)
axios
.put("http://127.0.0.1:8000/api/obtainFaceEmotionPrediction", fd)
.then((response) => {
if (response.data){
// window.alert(response.data.file);
// Here even if I were to delete everything in this if statement, it would still refresh the page
document.getElementById("article").innerHTML +=
"<img src='../frontend/public/pictures_saved/face_emotion/" + response.data.file + "'/>";
}
})
.catch((err) => console.log(err));
}
uploadMilitary(){
// code not finalized yet. will be similar with uploadSelfie function but will handle multiple pictures
}
generate_segment(){
let uiItems = [];
if(this.state.menu === 1){
uiItems.push(
<article id="article">
<h2 id="title">My ML Projects</h2>
<div id="options">
<ul>
<li><button className="active" id="about" onClick={(e) => this.update_menu_item(1)}>About</button></li>
<li><button id="face_emotion_detection" onClick={(e) => this.update_menu_item(2)}>Face Emotion Detection</button></li>
<li><button id="military_detection" onClick={(e) => this.update_menu_item(3)}>Military Detection</button></li>
</ul>
</div>
<p>The scope of this application is to showcase all of the ML projects that I have finished. The application features a WebApp where the frontend is designed in React and the backend in Django.</p>
<p>Using REST API I'm able to make a call from the frontend to the backend. There, depending on the selected project, a pre-trained ML model will take your input and serve your request.</p>
<p>If you want to check the source code for each of these projects and more, you can do so by using this link.</p>
</article>
)
}
else if(this.state.menu === 2){
uiItems.push(
<article id="article">
<h2 id="title">My ML Projects</h2>
<div id="options">
<ul>
<li><button id="about" onClick={(e) => this.update_menu_item(1)}>About</button></li>
<li><button className="active" id="face_emotion_detection" onClick={(e) => this.update_menu_item(2)}>Face Emotion Detection</button></li>
<li><button id="military_detection" onClick={(e) => this.update_menu_item(3)}>Military Detection</button></li>
</ul>
</div>
<p>The aim of this project is to detect both the face and the emotion of a person. Upload a selfie with you and see how the model works:</p>
<input
type="file"
name="picture"
id="picture"
placeholder="Upload a Selfie"
onChange={this.fileSelect}
/>
<input
type="button"
value="Submit"
onClick={(e) => this.uploadSelfie()}
/>
</article>
)
}
else{
uiItems.push(
<article id="article">
<h2 id="title">My ML Projects</h2>
<div id="options">
<ul>
<li><button id="about" onClick={(e) => this.update_menu_item(1)}>About</button></li>
<li><button id="face_emotion_detection" onClick={(e) => this.update_menu_item(2)}>Face Emotion Detection</button></li>
<li><button className="active" id="military_detection" onClick={(e) => this.update_menu_item(3)}>Military Detection</button></li>
</ul>
</div>
<p>The aim of this project is to detect military personnel from an input data. The input can be a picture or a video, however for this WebApp, currently it is supported only images input. If you would like to test the video feature, check out the git repository. Upload a picture with military personnel and see how the model works:</p>
<input
type="file"
name="picture"
id="picture"
placeholder="Upload a Picture with Military"
onChange={this.fileSelect}
/>
<input
type="button"
value="submit"
onClick={(e) => this.uploadMilitary()}
/>
</article>
)
}
return uiItems;
}
render(){
return(
<main>
{this.generate_segment()}
</main>
);
}
}
export default App;
The API returns a simple strings and save a picture on the SSD:
API view:
#api_view(['PUT'])
def obtainFaceEmotionPrediction(request):
if request.method == 'PUT':
if request.FILES.get("image", None) is not None:
img = request.FILES["image"]
img = img.read()
model = FaceEmotionRecognition()
img = model.detect_emotion(img)
dir = '../frontend/public/pictures_saved/face_emotion/'
for f in os.listdir(dir):
os.remove(os.path.join(dir, f))
cv2.imwrite('../frontend/public/pictures_saved/face_emotion/face_emotion_1.png', img)
return Response({'file': 'face_emotion_1.png'})
There is 1 error and 1 warning that I have in the console but I can't seem to understand if they are related to the problem:
Also, the API call happens smoothly:
I've tried to write and rewrite the code in different ways. I've tried to use event.preventDefault() and other variants but nothing seems to work. Something that I would like to add is that the current code works, and does exactly what I want it to do, except it reloads the page.
I would very much appreciate your help.
I wound the solution. The problem was in the API, to be more specific here:
cv2.imwrite('../frontend/public/pictures_saved/face_emotion/face_emotion_1.png', img)
Basically, in the Django server, I was saving a picture into the React server files. React "saw" that there were changes in the files (new files added/modified. not necessarily to edit a .js or .css. just add a simple .png) and re-rendered the whole page. Why does it work like that? I have no clue. I changed the path where the image would be saved to be outside the React's server files and it worked like a charm.

Javascript event-listener not working when trying to link to another page

I'm currently trying to display an individual.html page based off what the user clicks on a products.html page. For example, clicking on product1 would show the individual page for product1 and clicking on product2 would show the individual page for product2 etc...
I currently have a UI class in my app.js file which holds the logic for displaying information.
There is a function in there called displayIndividualProject() which has an event listener that says, "If a card is clicked (if (event.target.classList.contains('img-container'))), then inject this HTML structure into the individual.html" page. However, after I click on it, it's not injecting the sequence.
Also, displayIndividualProduct is meant for the individual.html page while displayProducts is meant for the product.html page.
Here is what part of my UI class looks like:
const individualProductsDOM = document.querySelector('.single-product');
class UI{
displayIndividualProduct(){
document.addEventListener("click", event => {
if (event.target.classList.contains('img-container'))
individualProductsDOM.innerHTML =
`
<div class='section-title'>
<h2>${product.title}</h2>
</div>
<div class='indi-img-container'>
<img src=${product.image} data-id='${product.id} alt="">
</div>
<div class="product-footer">
<h3>Estimated Cost: $ <span class='item-total'>0</span></h3>
<button class='bag-btn-2' data-id='${product.id}'>
<i class='fas fa-shopping-cart'></i>
add to cart
</button>
</div>
`
})
individualProductsDOM.innerHTML += `injected`;
}
displayProducts(products){
let result = '';
products.forEach(product => {
result += `
<article class="product" data-id='${product.id}'>
<div class='img-container'>
<a href='/individual.html'>
<img src=${product.image} alt="product" class='product-img' data-id='${product.id}'>
<button class='bag-btn' data-id='${product.id}'>
<i class='fas fa-shopping-cart'></i>
add to cart
</button>
</a>
</div>
<h3>${product.title}</h3>
<h4>$${product.price}/roll</h4>
</article>
`
});
//insert the products into the productsDOM
productsDOM.innerHTML = result;
}
}
and this is being called at the bottom of my JS through ui.displayIndividualProduct();
document.addEventListener("DOMContentLoaded", () => {
const ui = new UI();
const products = new Products();
// setup app
ui.setupAPP();
//get all products
products.getProducts().then(products => {
//first display, then save, and then connect the add cart buttons
ui.displayProducts(products);
Storage.saveProducts(products);
}).then( () => {
ui.getBagButtons();
});
ui.displayIndividualProduct();
});
any help would be appreciated!

change img that in class in class in id

I read about it but still I'm not able to make it work.
I have an image (empty heart) that I would like to change to a different image (full heart) in order to indicate it was added to the wish list.
here is my code:
<tbody class="mainImgs">
<div ng-repeat="party in showtop5" ng-class=party.name>
<img ng-src="{{party.image}}">
<h2 id="title">{{party.title}}</h2>
<h3 id="description">{{party.description}}</h2>
<button href="#" class="imageClick" ng-click="click(party.title, party.description, party.image)">
<img ng-src="../images/emptyHeart.png" id="heart" click="myFunction(party.name, imageClick)">
</button>
<img ng-src="{{party.img}}" id="pace">
</div>
</tbody>
and my script:
function myFunction(myclass1, myclass2){
document.getElementByClass(myclass1).getElementByClass(myclass2).getElementById("heart").src = "../images/fullHeart.png";
}
what did I do wrong?
without sending the class - and trying to find image only by id - t works only for the first object heart that is printed
I think if its just about changing image then it can be handled pretty straight forward:
In html
<button href="#" class="imageClick" ng-click="click(party.title, party.description, party.image)">
<img ng-src="{{imgPath}}" id="heart" ng-click="myFunction(party.name, imageClick)">
</button>
In Controller:
$scope.imgPath = "../images/emptyHeart.png";
$scope.myFunction= function (name,imageClick){
// use promise to monitor the server response of adding product to Wishlist
("YOUR_PROMISE_CALL").then(
function(success){
$scope.imgPath = "../images/fullHeart.png"; // Bingo !!
},
function(error){
// take appropriate action
},
)
}

Scraping links from html elements - casperjs

I am currently trying to scrape links and thumbnails from this SITE with the help of casperjs. I was able to easily figure out the html structure(shown below). I am trying to extract from all a tags the link found in the href attribute. I run my script but I get an error for video_links . How could I go about scraping all links and thumbnails and output in an array?
Error
TypeError: 'undefined' is not an object (evaluating 'video_links.length')
Script
var casper = require('casper').create({}),video_links,video_thumbnails;
//Functions
function getLinks() {
var element = document.querySelectorAll('.cne-episode-block a');
return Array.prototype.map.call(element, function(e) {
return e.getAttribute('href');
});
}
casper.start('http://video.wired.com/');
casper.then(function() {
video_links = this.evaluate(getLinks);
});
casper.run( this.echo(video_links.length + ' links found.') );
HTML
<div class="cne-thumb-grid-container cne-context-container">
<div class="cne-thumb cne-episode-block " data-videoid="551dc13461646d11aa020000">
<div class="cne-thumb-image cne-rollover" data-powertiptarget="551dc13461646d11aa020000">
<a class="cne-thumbnail cne-zoom-effect js-ajax-video-load" href="/watch/angry-nerd-will-netflix-s-daredevil-fly-or-flop" data-video-series="Angry Nerd" data-video-series-id="518d55c268f9dac897000003" data-video-id="551dc13461646d11aa020000" data-video-categories="[" Movies \u0026 TV "]">
<img class="cne-video-thumb" src="http://dwgyu36up6iuz.cloudfront.net/heru80fdn/image/upload/c_fill,d_placeholder_thescene.jpg,fl_progressive,g_face,h_151,q_80,w_270/v1428076783/wired_angry-nerd-will-netflix-s-daredevil-fly-or-flop.jpg" alt="Will Netflix’s Daredevil Fly or Flop?">
<div class="cne-thumbnail-play">Play</div>
</a>
</div>
<div class="cne-thumb-title the-thumb-title">
<a class="js-ajax-video-load" href="/watch/angry-nerd-will-netflix-s-daredevil-fly-or-flop" data-video-id="551dc13461646d11aa020000">Will Netflix’s Daredevil Fly or Flop?</a>
<div class="cne-thumb-subtitle">
Angry Nerd
</div>
</div>
<div id="551dc13461646d11aa020000" class="cne-thumb-rollover">
<div class="cne-thumb-rollover-box">
<span class="cne-rollover-category"> Movies & TV </span>
<span class="cne-rollover-name"> Will Netflix’s Daredevil Fly or Flop? </span>
<span class="cne-rollover-description"> If Netflix’s new Daredevil series is anything like Ben Affleck’s Daredevil film, we’re all in trouble. Angry Nerd explains what the latest incarnation needs to get right to make sure the man without fear doesn’t turn into a total flop. </span>
</div>
</div>
</div>
</div>
If the selectors are on the same level, you will only need one of them. So just use either cne-thumb or cne-episode-block in your querySelectorAll not both.

Running click functions on every instance of listing instead of current

I have a listing of articles here, and I can't figure out how to execute the ng-click function calls on every new article inside the ng-repeat. Right now it works for existing articles, but when new articles are added dynamically (via AJAX), I need those to have the same functionality too.
For example: the ng-click function calls on the "+" sign to reveal social buttons seem to not work once new articles are inserted via AJAX (ie: delete articles, and let list be populated again with new elements)
Does AngularJS provide any tools to do that?
<div>
<div>
<input type="text" ng-model="search">
<span>{{filtered.length}} article(s)</span>
</div>
<div article-listing ng-repeat="article in filtered = (wikiArticles | filter:search)">
<!--Individual article begin-->
<span>
{{article.title}}
</span>
<div>
<a ng-click="articles.removeArticle($index)" title="Delete">
<span>✖</span>
</a>
<a ng-click="articles.toggleShare(article)">
<span class="plus-sign" title="Share">✖</span>
<div social-share ng-show="article.socialShare">
<div ng-click="socialShare = !socialShare" class="addthis_toolbox addthis_default_style addthis_32x32_style"
addthis:title="{{article.title}}" addthis:description="{{article.extract}}" addthis:url="{{article.url}}">
<a class="addthis_button_facebook"></a>
<a class="addthis_button_twitter"></a>
<a class="addthis_button_google_plusone_share"></a>
<a class="addthis_button_reddit"></a>
<a class="addthis_button_hackernews"></a>
</div>
</div>
</a>
</div>
<div>{{article.extract}}</div>
<!--Individual article end-->
</div>
</div>
Code for ng-click calls that don't seem to work for new article insertions
$scope.articles = (function() {
return {
shuffleArticles : function() {
$scope.wikiArticles.reverse();
},
removeArticle : function(index) {
$scope.wikiArticles.splice(index, 1);
$scope.fireAPICalls();
},
toggleShare : function(currArticle) {
var previousState = currArticle.socialShare;
angular.forEach($scope.wikiArticles, function(article) {
article.socialShare = false;
});
currArticle.socialShare = previousState ? false : true;
}
}
})();
Your ng-click calls are actually working- you can watch the ng-show toggle in the debugger.
The problem is that there is nothing to display on the new items you add.
The articles you initially add all have their icons populated with the .addthis classes, for instance here's your Facebook icon element:
<a class="addthis_button_facebook at300b" title="Facebook" href="#">
<span class=" at300bs at15nc at15t_facebook">
<span class="at_a11y">Share on facebook</span>
</span>
</a>
at300bs includes the following css which displays the image:
background: url(widget058_32x32.gif) no-repeat left!important;
However as you add new items, you aren't including the needed .addthis classes to them. Their elements look like this:
<a class="addthis_button_facebook"></a>
So ng-show has nothing to display (it shows a 0x0 div).
Add the .addthis classes to your new elements as you add them and you'll be all set.

Categories

Resources