Spotify API - Audio Player - javascript
I am relatively new to Javascript, I am practicing with a program that is able to pull data from Spotify API starting with Genre, then Playlists by Genre, Tracks, Track details, and an audio tag player to play a preview of the track.
I am able to do the Genre, Tracks, Track Details, but can't figure out the audio tag being linked to the track. The two issues, I have:
How to generate the audio tag based on user selection, and then reset it once a new selection is made
How to Get the Preview for the track based on user selection (for now I have the src for the audio tag as a random track.
const APIController = (function() {
const clientId = 'XX';
const clientSecret = 'XX';
//Fetch data from spotify api; private methods
const _getToken = async () => {
const result = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
'Content-Type' : 'application/x-www-form-urlencoded',
'Authorization' : 'Basic ' + btoa( clientId + ':' + clientSecret)
},
body: 'grant_type=client_credentials'
});
const spot = await result.json();
return spot.access_token;
}
const _getGenres = async (token) => {
const limit = 10;
const result = await fetch(`https://api.spotify.com/v1/browse/categories?country=CA&limit=${limit}`, {
method: 'GET',
headers: { 'Authorization' : 'Bearer ' + token}
});
const spot = await result.json();
return spot.categories.items;
}
const _getPlaylistByGenre = async (token, genreId) => {
const limit = 10;
const result = await fetch(`https://api.spotify.com/v1/browse/categories/${genreId}/playlists?limit=${limit}`, {
method: 'GET',
headers: { 'Authorization' : 'Bearer ' + token}
});
const spot = await result.json();
return spot.playlists.items;
}
const _getTracks = async (token, tracklist) => {
const limit = 30;
const result = await fetch(`${tracklist}?limit=${limit}`, {
method: 'GET',
headers: { 'Authorization' : 'Bearer ' + token}
});
const spot = await result.json();
return spot.items;
}
const _getTrack = async (token, trackdetail) => {
const result = await fetch(`${trackdetail}`, {
method: 'GET',
headers: { 'Authorization' : 'Bearer ' + token}
});
const spot = await result.json();
return spot;
}
const _getPreview = async (token, trackpreview) => {
const result = await fetch(`${trackpreview}`, {
method: 'GET',
headers: { 'Authorization' : 'Bearer ' + token}
});
const spot = await result.json();
return spot.preview;
}
return {
getToken() {
return _getToken();
},
getGenres(token) {
return _getGenres(token);
},
getPlaylistByGenre(token, genreId) {
return _getPlaylistByGenre(token, genreId);
},
getTracks(token, tracklist) {
return _getTracks(token, tracklist);
},
getTrack(token, trackdetail) {
return _getTrack(token, trackdetail);
},
getPreview(token, trackpreview) {
return _getPreview(token, trackpreview);
}
}
})();
// UI Module
const UIController = (function() {
//object to hold references to html selectors
const DOMElements = {
selectGenre: '#select_genre',
selectPlaylist: '#select_playlist',
buttonSubmit: '#btn_submit',
divSongDetail: '#song-detail',
hfToken: '#hidden_token',
divSonglist: '.song-list',
preview:'.songpreview'
}
//public methods
return {
//method to get input fields
inputField() {
return {
genre: document.querySelector(DOMElements.selectGenre),
playlist: document.querySelector(DOMElements.selectPlaylist),
tracks: document.querySelector(DOMElements.divSonglist),
submit: document.querySelector(DOMElements.buttonSubmit),
songDetail: document.querySelector(DOMElements.divSongDetail),
songPreview: document.querySelector(DOMElements.preview)
}
},
// need methods to create select list option
createGenre(text, value) {
const html = `<option value="${value}">${text}</option>`;
document.querySelector(DOMElements.selectGenre).insertAdjacentHTML('beforeend', html);
},
createPlaylist(text, value) {
const html = `<option value="${value}">${text}</option>`;
document.querySelector(DOMElements.selectPlaylist).insertAdjacentHTML('beforeend', html);
},
// need method to create a track list group item
createTrack(id, name) {
const html = `${name} `;
document.querySelector(DOMElements.divSonglist).insertAdjacentHTML('beforeend', html);
},
// need method to create the song detail
createTrackDetail(img, title, artist) {
const detailDiv = document.querySelector(DOMElements.divSongDetail);
// any time user clicks a new song, we need to clear out the song detail div
detailDiv.innerHTML = '';
const html =
`
<div class="songimg">
<img src="${img}" alt=""width="100px" height="100px">
</div>
<div class="songname"><span>
<label for="Genre" style="font-size:20px; color:black" class="form-label">${title}</label>
</span>
</div>
<div class="artistname"><span>
<label for="artist" style="color:blue" class="form-label">${rtist}</label>
</span></div>
`;
detailDiv.insertAdjacentHTML('beforeend', html)
},
createPreview() {
const html = `<audio class="preview" id="newplayer" controls onplay="playtrack()" onpause="pausetrack()" type="audio/mpeg" src="https://p.scdn.co/mp3-preview/ad57003c15e607c0d74c8a25ed7581c770b28a9a?cid=774b29d4f13844c495f206cafdad9c86" preload="auto"></audio>`;
document.querySelector(DOMElements.songPreview).insertAdjacentHTML('beforeend', html);
},
//method to reset fields
resetPreview() {
this.inputField().songPreview.innerHTML = '';
},
resetTrackDetail() {
this.inputField().songDetail.innerHTML = '';
},
resetTracks() {
this.inputField().tracks.innerHTML = '';
this.resetTrackDetail();
const spotpreview=document.getElementById("newplayer").classList;
spotpreview.remove("showme");
document.getElementsByTagName("body")[0].style="background-image:url('hands.jpeg');";
document.getElementsByTagName("body")[0].class="backgrd3";
},
resetPlaylist() {
this.inputField().playlist.innerHTML = '';
this.resetTracks();
},
storeToken(value) {
document.querySelector(DOMElements.hfToken).value = value;
},
getStoredToken() {
return {
token: document.querySelector(DOMElements.hfToken).value
}
}
}
})();
const APPController = (function(UICtrl, APICtrl) {
// get input field object ref
const DOMInputs = UICtrl.inputField();
// get genres on page load
const loadGenres = async () => {
//get the token
const token = await APICtrl.getToken();
//store the token onto the page
UICtrl.storeToken(token);
//get the genres
const genres = await APICtrl.getGenres(token);
//populate our genres select element
genres.forEach(element => UICtrl.createGenre(element.name, element.id));
}
// create genre change event listener
DOMInputs.genre.addEventListener('change', async () => {
//reset the playlist
UICtrl.resetPlaylist();
//get the token that's stored on the page
const token = UICtrl.getStoredToken().token;
// get the genre select field
const genreSelect = UICtrl.inputField().genre;
// get the genre id associated with the selected genre
const genreId = genreSelect.options[genreSelect.selectedIndex].value;
// ge the playlist based on a genre
const playlist = await APICtrl.getPlaylistByGenre(token, genreId);
// create a playlist list item for every playlist returned
playlist.forEach(p => UICtrl.createPlaylist(p.name, p.tracks.href));
});
// create submit button click event listener
DOMInputs.submit.addEventListener('click', async (e) => {
// prevent page reset
e.preventDefault();
// clear tracks
UICtrl.resetTracks();
//get the token
const token = UICtrl.getStoredToken().token;
// get the playlist field
const playlistSelect = UICtrl.inputField().playlist;
// get track endpoint based on the selected playlist
const tracksEndPoint = playlistSelect.options[playlistSelect.selectedIndex].value;
// get the list of tracks
const tracks = await APICtrl.getTracks(token, tracksEndPoint);
// create a track list item
tracks.forEach(el => UICtrl.createTrack(el.track.href, el.track.name))
});
// create song selection click event listener
DOMInputs.tracks.addEventListener('click', async (e) => {
// prevent page reset
e.preventDefault();
UICtrl.resetTrackDetail();
// get the token
const token = UICtrl.getStoredToken().token;
// get the track endpoint
const trackEndpoint = e.target.id;
//get the track object
const track = await APICtrl.getTrack(token, trackEndpoint);
// load the track details
UICtrl.createTrackDetail(track.album.images[2].url, track.name, track.artists[0].name);
});
// create song preview selection click event listener
DOMInputs.songPreview.addEventListener('click', async (e) => {
// prevent page reset
e.preventDefault();
UICtrl.resetPreview();
// get the token
const token = UICtrl.getStoredToken().token;
// get the playlist field
const previewsong = UICtrl.inputField().songPreview;
const previewEndPoint = previewsong.options[previewsong.selectedIndex].value;
// get the list of tracks
const tracks = await APICtrl.getTracks(token, previewEndPoint);
// create a track list item
tracks.forEach(el => UICtrl.createTrack(el.track.href, el.track.name))
});
return {
init() {
console.log('App is starting');
loadGenres();
}
}
})(UIController, APIController);
// will need to call a method to load the genres on page load
APPController.init();
Related
Fetching Friends from api and Display on the frontend
i'M working on a Chat Application project but im getting this error of fetching friends from the backend(node) I'm getting the friends data on the console but i'm unable to display it. this is my Context and States export const Messenger = () => { // Context State const { friends, setFriends, authInfo } = useAuth(); const [loggedUser, setLoggedUser] = useState(); const { updateNotification } = useNotification(); const fetchMessengerUsers = async () => { try { const token = getToken(); const config = { headers: { authorization: "Bearer " + token, }, }; const { data } = await client("/get-friends", config); console.log(data); setFriends(data); } catch (error) { updateNotification("error", "Failed To load the Chat"); } }; useEffect(() => { setLoggedUser(localStorage.getItem("auth-token")); fetchMessengerUsers(); }, []); then in return i'm mapping all friends to display them <div className="friends"> {friends && friends.length > 0 ? friends.map((fd) => ( <div className="hover-friend"> <Friends friend={fd} /> </div> )) : "No Friend"} </div> It displays No Friend on the browser this link shows how it appears on the browser
just change your fetchMessengerUsers function. you need to set setFriends(data.friends) const fetchMessengerUsers = async () => { try { const token = getToken(); const config = { headers: { authorization: "Bearer " + token, }, }; const { data } = await client("/get-friends", config); console.log(data); setFriends(data.friends); // you have to set friends array here, earlier you were setting the entire object. } catch (error) { updateNotification("error", "Failed To load the Chat"); } };
How to retrieve more than 1000 otherContacts from Google API with pageToken?
I have a react App which I will be using to bring in google other contacts and I see that theres a limit for 1000 contacts in each call to google People Api's. const getGoogleContacts = async (token) => { if(!token) throw new Error('Google Auth Token Missing'); let params = {"readMask" : "emailAddresses,names", "pageSize" : "1000"}; let url = `https://people.googleapis.com/v1/otherContacts?${new URLSearchParams(params)}`; let headers = { Authorization: `Bearer ${token}`}; let rawData = await fetch(url, { headers }) .then(res => res.text()); let parsedJson = JSON.parse(rawData); let nextPageCheck = checkForNextPageToken(parsedJson); if(nextPageCheck){ //get the next page contacts. } return handleRawContactData(parsedJson); } const checkForNextPageToken = (response) => { if(response.hasOwnProperty('nextPageToken')){ return true; } return false; }
How do I get my data to display on flash cards using JavaScript?
Right now I'm working on full stack application that uses JS on the front and back end. This app lets a user generate their own set of flash cards. Whenever the user clicks "View Cards" data will then be fetched and will display the question and answer on each side of the card. It's supposed to display one card at a time and allows the user to scroll through other cards using either the "Previous" or "Next" buttons. I'm able to successfully fetch data convert it to JSON and can get at least one item from the data base display properly. The problem is whenever I try to scroll through the cards. I'm only able to scroll through some of the cards before the browser returns an error. I've even noticed that some of the cards won't render both sides properly. How could I address these issues? const flashCard = document.querySelector(".flashcard"); const flipBtn = document.querySelector(".flip-btn"); const prevBtn = document.querySelector(".prev-btn"); const nextBtn = document.querySelector(".next-btn"); let frontOfCard = document.querySelector(".front-content"); let backOfCard = document.querySelector(".back-content"); const displayCards = () => { getCardInfo() flipBtn.innerHTML = "Flip" flipBtn.removeEventListener("click", displayCards) } flipBtn.addEventListener("click", displayCards) const flash = () => { if (flashCard.style.transform != "rotateX(180deg)") { flashCard.style.transform = "rotateX(180deg)" } else { flashCard.style.transform = "none" } } const getCardInfo = async () => { const itemBody = { method: "PUT", headers: { Accept: "application/json", "Content-Type": "application/json" } } const data = await fetch(window.location.href, itemBody) const jsonData = await data.json() console.log(jsonData) let idx = 0; frontOfCard.innerHTML = jsonData[idx].Answer backOfCard.innerHTML = jsonData[idx].Question flashCard.style.display = "block"; flipBtn.addEventListener("click", flash); scrollThroughCards(idx, jsonData); } function scrollThroughCards(idx, data) { prevBtn.addEventListener("click", (e) => { flashCard.style.display = "none" setTimeout(() => { frontOfCard.innerHTML = data[idx--].Answer backOfCard.innerHTML = data[idx--].Question flashCard.style.display = "block" }, 1000) e.preventDefault() }) nextBtn.addEventListener("click", (e) => { flashCard.style.display = "none" setTimeout(() => { frontOfCard.innerHTML = data[idx++].Answer backOfCard.innerHTML = data[idx++].Question flashCard.style.display = "block" }, 1000) e.preventDefault() }) } app.get("/card/:id", checkAuthenticated, async (req,res) => { const { id } = req.params const data = await Card.findAll({ where: { NoteId: id } }); res.render("cards-page", { noteid: id, Cards: data }) }); app.put("/card/:id", checkAuthenticated, async (req,res) => { const { id } = req.params const data = await Card.findAll({ where: { NoteId: id } }); res.json(data) }) app.post("/card/:id", checkAuthenticated, async (req, res) => { const { Question, Answer, NoteId } = req.body; const newCard = await Card.create({ Question, Answer, NoteId }); res.redirect(`/card/${NoteId}`) });
In the scrollThroughCards function, boundary checks were not performed and the increment and decrement operators were misused. function scrollThroughCards(idx, data) { prevBtn.addEventListener("click", (e) => { // there's no more card on the left of index 0 // so exit the function early if (idx <= 0) return; flashCard.style.display = "none" setTimeout(() => { idx--; // decrease the index first // then use the modified index frontOfCard.innerHTML = data[idx].Answer backOfCard.innerHTML = data[idx].Question flashCard.style.display = "block" }, 1000) e.preventDefault() }) nextBtn.addEventListener("click", (e) => { // there's no more cards beyond the end of the list // so exit the function early if (idx >= data.length - 1) return; flashCard.style.display = "none" setTimeout(() => { idx++; // increase the index first // then use the modified index next frontOfCard.innerHTML = data[idx].Answer backOfCard.innerHTML = data[idx].Question flashCard.style.display = "block" }, 1000) e.preventDefault() }) }
VSCode Extension - TreeView with HTTP Items; Only return when request finished
I want to create a VSCode extension for displaying all open issues (data provided by selfhosted gitea). The ChildItemProviderInterface for my TreeView implements two methods. They can't be set to async (at least I couldn't figure out how). I need to return my child array once the HTTP Request finished (thenable) refresh(): Thenable<vscode.TreeItem[]> { axios.get("http://GITURL.com/api/v1/repos/REPOOWNER/REPOSITORY/issues", { headers: { Authorization: "token TOKEN" } }).then(res => { let issueList: any[] = []; for (const issue of res.data) { const title = issue["title"]; const id = issue["id"]; const body = issue["body"]; const state = issue["state"]; const assignee = issue["assignee"] === null ? "None" : "Username"; const label = "adf"; const issueForList = new Issue(title, id, body, state, assignee, label, vscode.TreeItemCollapsibleState.None); this.issueList.push(issueForList); } return this.issueList; }).catch(err => { console.log(err); return null; }); }
Can't figure out why my app.get is being run twice?
I have a app.get which inside of it is quite a bit of logic. Which everything works great aside from some of the logic being called twice for some reason. I have noticed when I was saving something to by db that it would save two rows. So I put a console.log in that area and sure enough it was logging it twice. Any reason why this is happening? app.get('/shopify/callback', (req, res) => { const { shop, hmac, code, state } = req.query; const stateCookie = cookie.parse(req.headers.cookie).state; if (state !== stateCookie) { return res.status(403).send('Request origin cannot be verified'); } if (shop && hmac && code) { // DONE: Validate request is from Shopify const map = Object.assign({}, req.query); delete map['signature']; delete map['hmac']; const message = querystring.stringify(map); const providedHmac = Buffer.from(hmac, 'utf-8'); const generatedHash = Buffer.from( crypto .createHmac('sha256', config.oauth.client_secret) .update(message) .digest('hex'), 'utf-8' ); let hashEquals = false; try { hashEquals = crypto.timingSafeEqual(generatedHash, providedHmac) } catch (e) { hashEquals = false; }; if (!hashEquals) { return res.status(400).send('HMAC validation failed'); } // DONE: Exchange temporary code for a permanent access token const accessTokenRequestUrl = 'https://' + shop + '/admin/oauth/access_token'; const accessTokenPayload = { client_id: config.oauth.api_key, client_secret: config.oauth.client_secret, code, }; request.post(accessTokenRequestUrl, { json: accessTokenPayload }) .then((accessTokenResponse) => { const accessToken = accessTokenResponse.access_token; // DONE: Use access token to make API call to 'shop' endpoint const shopRequestUrl = 'https://' + shop + '/admin/shop.json'; const shopRequestHeaders = { 'X-Shopify-Access-Token': accessToken, } request.get(shopRequestUrl, { headers: shopRequestHeaders }) .then((shopResponse) => { const response = JSON.parse(shopResponse); const shopData = response.shop; console.log('BEING CALLED TWICE...') res.render('pages/brand_signup',{ shop: shopData.name }) }) .catch((error) => { res.status(error.statusCode).send(error.error.error_description); }); }) .catch((error) => { res.status(error.statusCode).send(error.error.error_description); }); } else { res.status(400).send('Required parameters missing'); } });