Spotify api add track to playlist - javascript

I am using ReactJS and trying to make simple site using the Spotify api.
I am also using the js package spotify-web-api-js. I have succeeded to get the current song playing and able to show it in the browser.
getNowPlaying(){
spotifyApi.getMyCurrentPlaybackState()
.then((response) => {
this.setState({
nowPlaying: {
name: response.item.name,
albumArt: response.item.album.images[0].url
}
});
})
}
//this is from the github link above
Constr.prototype.getMyCurrentPlaybackState = function(options, callback) {
var requestData = {
url: _baseUri + '/me/player'
};
return _checkParamsAndPerformRequest(requestData, options, callback);
};
But the problem I am facing is to add a track to my playlist. I have tried to use the function replaceTracksInPlaylist from spotify-web-api-js but I cant manage to get it to work, I always get the 403 Forbidden error.
addToPlayList(){
spotifyApi.addTracksToPlaylist(listID, listOfSongID, callback);
}
//this is from the github link above
Constr.prototype.replaceTracksInPlaylist = function(playlistId, uris, callback) {
var requestData = {
url: _baseUri + '/playlists/' + playlistId + '/tracks',
type: 'PUT',
postData: { uris: uris }
};
return _checkParamsAndPerformRequest(requestData, {}, callback);
};
The first example also requires authentication as a access token but why does it not work when adding to a playlist?
I have also changed the scopes to the correct ones that the api documentation says i should use, playlist-modify-public and playlist-modify-private
var scope = 'playlist-modify-public playlist-modify-private user-read-private user-read-email user-read-playback-state';
I can test the api call from the api documentation site and insert my oauth token, track and the playlist id there, and that works fine, I also get this back from the call. I cant think of anything else to try and need some help figuring out the next step.

Related

How to access the trello API from within a powerup?

How can I make queries against the trello API from within a powerup? This seems like such an obvious question but it doesn't seem to be covered that I can find.
My simple powerup looks like this so far:
var boardButtonCallback = function(t){
return t.popup({
title: 'Tools',
items: [
{
text: 'Hide Duplicates',
callback: function(t){
var cardQueryCb = function(result){
console.log(result);
}
var cardQ = 'https://trello.com/1/boards/[board_id]/cards/all';
fetch(cardQ).then(function(response) {
return response.json();
}).then(function(data) {
console.log(data);
});
return t.cards('id', 'name')
.then(cardQueryCb);
}
}
]
});
};
TrelloPowerUp.initialize({
'board-buttons': function(t, options){
return [{
text: 'Duplicates',
callback: boardButtonCallback
}];
}
});
The response object after the call to fetch says the call is unauthorized.
I would have thought that calling this code from within the context of the power up would be considered authorized. While I'm logged into trello, I'm able to put that address into my browser and get a valid response - how come the javascript call doesn't also result in a valid response?
More importantly, how can I get a successful response from that URL?
Since your power-up is run through an iframe, it's not actually coming from the Trello page itself so you need to specify your API key and token in the GET URL.
Example:
https://api.trello.com/1/boards/560bf4298b3dda300c18d09c?fields=name,url&key={YOUR-API-KEY}&token={AN-OAUTH-TOKEN}
The info for getting your API key and token can be found here: https://trello.readme.io/v1.0/reference#api-key-tokens

Getting x from remote sources and mirroring on to a list

Currently I have this, if with the full app it will create a post with my chosen parameters, however I am very new with vue.js, My aim is to be able to have a text file of such (or other way of storing (json etc)) the values, and then having the js script iterate through the file and display as cards, so for example in the file I would have
"Mark", "http://google.com", "5556", "image"
Or of course using json or similar, I'm up to what ever but my problem is, I don't know how to get values from a remote source and mirror it on to the document, can anyone help?, for clarity here's the snippet of code that I'm using
var app = new Vue({
el: '#app',
data: {
keyword: '',
postList: [
new Post(
'Name',
'Link',
'UID',
'Image'),
]
},
});
-- EDIT --
I'd like to thank the user Justin MacArthur for his quick answer, if you or anyone else doesn't mind answering another one of my painfully incompetent questions. This is the function that adds the cards in a nutshell
var Post = function Post(title, link, author, img) {
_classCallCheck(this, Post);
this.title = title;
this.link = link;
this.author = author;
this.img = img;
};
I can now get the data from the text file, meaning I could do, and assuming I have response defined (that being the http request) it'll output the contents of the file, how would I do this for multiple cards- as, as one would guess having a new URL for each variable in each set of four in each card is not just tedious but very inefficient.
new Post(
response.data,
)
The solution you're looking for is any of the AJAX libraries available. Vue used to promote vue-resource though it recently retired that support in favor of Axios
You can follow the instructions on the github page to install it in your app and the usage is very simple.
// Perform a Get on a file/route
axios.get(
'url.to.resource/path',
{
params: {
ID: 12345
}
}
).then(
// Successful response received
function (response) {
console.log(response);
}
).catch(
// Error returned by the server
function (error) {
console.log(error);
}
);
// Perform a Post on a file/route
// Posts don't need the 'params' object as the second argument is sent as the request body
axios.post(
'url.to.resource/path',
{
ID: 12345
}
).then(
// Successful response received
function (response) {
console.log(response);
}
).catch(
// Error returned by the server
function (error) {
console.log(error);
}
);
Obviously in the catch handler you'd have your error handing code, either an alert or message appearing on the page. In the success you could have something along the lines of this.postList.push(new Post(response.data.name, response.data.link, response.data.uid, response.data.image));
To make it even easier you can assign axios to the vue prototype like this:
Vue.prototype.$http = axios
and make use of it using the local vm instance
this.$http.post("url", { data }).then(...);
EDIT:
For your multi-signature function edit it's best to use the arguments keyword. In Javascript the engine defines an arguments array containing the parameters passed to the function.
var Post = function Post(title, link, author, img) {
_classCallCheck(this, Post);
if(arguments.length == 1) {
this.title = title.title;
this.link = title.link;
this.author = title.author;
this.img = title.img;
} else {
this.title = title;
this.link = link;
this.author = author;
this.img = img;
}
};
Be careful not to mutate the arguments list as it's a reference list to the parameters themselves so you can overwrite your variables easily without knowing it.

ng-repeat repeats, but is empty

I'm using the Soundcloud API to fetch tracks for a query typed into a searchbox like so:
$scope.tracks = [];
$scope.updateResults=function(song){
if(song.length==0){
$scope.tracks = [];
}
if(song.length>3){
SC.get('/tracks', { q: song, license: 'cc-by-sa' }, function(tracks) {
$scope.tracks = tracks;
//$scope.$apply() doesn't work either
});
}
}
$scope.updateResults is called whenever something is typed into the searchbar. This all works fine. I've even tried logging $scope.tracks, which is an array, and it is populated with the tracks matching the search result. According to the Soundcloud API reference, each track has a title property. I tested this by doing:
console.log($scope.tracks[0].title), and I got a nice string with the title.
Now in my ng-repeat, which is written like so:
<ul>
<li ng-repeat="track in tracks">{{track.title}}</li>
</ul>
I get several bullet points but they are all empty. In other words {{track.title}} doesn't exist? I've tested that this is a valid property. Additionally, I know that $scope.tracks is being populated because there are indeed list items, but they are all empty.
Thanks for any help!
EDIT: I did some research on ng-repeat and learned that it watches for updates such as .push()... should still not explain this weird behavior, but it might help identify the problem.
This isn't going to be the most technical explanation of what's going on, so I appreciate all edits. Since the Soundcloud Javascript SDK doesn't use $http to get the tracks, the scope doesn't know it needs to update when SC.get returns the tracks. In other words, even though $scope.tracks is populated, it doesn't know to update the view. I would recommend writing a service that handles all GET/POST/PUT/DELETE requests to Soundcloud. Here is a simple example using callbacks:
.factory('Soundcloud', function($http, $rootScope, $timeout, $q) {
var request = function(method, path, params, callback) {
params.client_id = sc.soundcloud.client_id;
params.oauth_token = sc.soundcloud.access_token;
$http({
method: method,
url: sc.soundcloud.api.host + path,
params: params
})
.success(callback);
};
return {
get: function(path, params, callback) {
request('GET', path, params, callback);
},
put: function(path, params, callback) {
request('PUT', path, params, callback);
},
post: function(path, params, callback) {
request('POST', path, params, callback);
},
delete: function(path, params, callback) {
request('DELETE', path, params, callback);
}
};
})
Once you inject the service into your controller, you get tracks like this:
Soundcloud.get('/tracks', {limit: 5}, function(tracks) {
$scope.tracks = tracks;
});
Just set up where the service gets your client_id and oauth_token. Then the path is whatever endpoint you want and params is whatever parameters you want to pass to SC.

Soundcloud API list my stream

I'm using the JavaScript SDK for the Soundcloud API and trying to populate the top 10 tracks from authenticated users stream.
Currently I am implementing this with:
SC.get('/tracks', { limit: '10'}, function(tracks) {
//Do stuff with tracks
});
However, this only returns the most recent 10 tracks/sounds uploaded to SoundCloud. Is there an easy way to populate my stream?
I was looking for the same thing, but there is no way to get the complete user's stream. You can get the users activities from
https://api.soundcloud.com/me/activities.json
But there are not all songs listed from your stream. I hope the SoundCloud devs will make that thing better in the future.
Try to use this code
fetch: function () {
var self = this;
SC.get("/tracks", { limit: '10'}, function (data) {
self.tracks = $.map(data, function (track) {
return {
title: track.title
};
});
self.attachTemplate();
});
}
soundcloud.init({
template: $('#tracks-template').html(),
container: $('ul.soundcloud')
});
And show the item title in your HTML page.

AngularJS: How to send auth token with $resource requests?

I want to send an auth token when requesting a resource from my API.
I did implement a service using $resource:
factory('Todo', ['$resource', function($resource) {
return $resource('http://localhost:port/todos.json', {port:":3001"} , {
query: {method: 'GET', isArray: true}
});
}])
And I have a service that stores the auth token:
factory('TokenHandler', function() {
var tokenHandler = {};
var token = "none";
tokenHandler.set = function( newToken ) {
token = newToken;
};
tokenHandler.get = function() {
return token;
};
return tokenHandler;
});
I would like to send the token from tokenHandler.get with every request send via the Todo service. I was able to send it by putting it into the call of a specific action. For example this works:
Todo.query( {access_token : tokenHandler.get()} );
But I would prefer to define the access_token as a parameter in the Todo service, as it has to be sent with every call. And to improve DRY.
But everything in the factory is executed only once, so the access_token would have to be available before defining the factory and it cant change afterwards.
Is there a way to put a dynamically updated request parameter in the service?
Thanks to Andy Joslin. I picked his idea of wrapping the resource actions. The service for the resource looks like this now:
.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
var resource = $resource('http://localhost:port/todos/:id', {
port:":3001",
id:'#id'
}, {
update: {method: 'PUT'}
});
resource = tokenHandler.wrapActions( resource, ["query", "update"] );
return resource;
}])
As you can see the resource is defined the usual way in the first place. In my example this includes a custom action called update. Afterwards the resource is overwritten by the return of the tokenHandler.wrapAction() method which takes the resource and an array of actions as parameters.
As you would expect the latter method actually wraps the actions to include the auth token in every request and returns a modified resource. So let's have a look at the code for that:
.factory('TokenHandler', function() {
var tokenHandler = {};
var token = "none";
tokenHandler.set = function( newToken ) {
token = newToken;
};
tokenHandler.get = function() {
return token;
};
// wrap given actions of a resource to send auth token with every
// request
tokenHandler.wrapActions = function( resource, actions ) {
// copy original resource
var wrappedResource = resource;
for (var i=0; i < actions.length; i++) {
tokenWrapper( wrappedResource, actions[i] );
};
// return modified copy of resource
return wrappedResource;
};
// wraps resource action to send request with auth token
var tokenWrapper = function( resource, action ) {
// copy original action
resource['_' + action] = resource[action];
// create new action wrapping the original and sending token
resource[action] = function( data, success, error){
return resource['_' + action](
angular.extend({}, data || {}, {access_token: tokenHandler.get()}),
success,
error
);
};
};
return tokenHandler;
});
As you can see the wrapActions() method creates a copy of the resource from it's parameters and loops through the actions array to call another function tokenWrapper() for every action. In the end it returns the modified copy of the resource.
The tokenWrappermethod first of all creates a copy of preexisting resource action. This copy has a trailing underscore. So query()becomes _query(). Afterwards a new method overwrites the original query() method. This new method wraps _query(), as suggested by Andy Joslin, to provide the auth token with every request send through that action.
The good thing with this approach is, that we still can use the predefined actions which come with every angularjs resource (get, query, save, etc.), without having to redefine them. And in the rest of the code (within controllers for example) we can use the default action name.
Another way is to use an HTTP interceptor which replaces a "magic" Authorization header with the current OAuth token. The code below is OAuth specific, but remedying that is a simple exercise for the reader.
// Injects an HTTP interceptor that replaces a "Bearer" authorization header
// with the current Bearer token.
module.factory('oauthHttpInterceptor', function (OAuth) {
return {
request: function (config) {
// This is just example logic, you could check the URL (for example)
if (config.headers.Authorization === 'Bearer') {
config.headers.Authorization = 'Bearer ' + btoa(OAuth.accessToken);
}
return config;
}
};
});
module.config(function ($httpProvider) {
$httpProvider.interceptors.push('oauthHttpInterceptor');
});
I really like this approach:
http://blog.brunoscopelliti.com/authentication-to-a-restful-web-service-in-an-angularjs-web-app
where the token is always automagically sent within the request header without the need of a wrapper.
// Define a new http header
$http.defaults.headers.common['auth-token'] = 'C3PO R2D2';
You could create a wrapper function for it.
app.factory('Todo', function($resource, TokenHandler) {
var res= $resource('http://localhost:port/todos.json', {
port: ':3001',
}, {
_query: {method: 'GET', isArray: true}
});
res.query = function(data, success, error) {
//We put a {} on the first parameter of extend so it won't edit data
return res._query(
angular.extend({}, data || {}, {access_token: TokenHandler.get()}),
success,
error
);
};
return res;
})
I had to deal with this problem as well. I don't think if it is an elegant solution but it works and there are 2 lines of code :
I suppose you get your token from your server after an authentication in SessionService for instance. Then, call this kind of method :
angular.module('xxx.sessionService', ['ngResource']).
factory('SessionService', function( $http, $rootScope) {
//...
function setHttpProviderCommonHeaderToken(token){
$http.defaults.headers.common['X-AUTH-TOKEN'] = token;
}
});
After that all your requests from $resource and $http will have token in their header.
Another solution would be to use resource.bind(additionalParamDefaults), that return a new instance of the resource bound with additional parameters
var myResource = $resource(url, {id: '#_id'});
var myResourceProtectedByToken = myResource.bind({ access_token : function(){
return tokenHandler.get();
}});
return myResourceProtectedByToken;
The access_token function will be called every time any of the action on the resource is called.
I might be misunderstanding all of your question (feel free to correct me :) ) but to specifically address adding the access_token for every request, have you tried injecting the TokenHandler module into the Todo module?
// app
var app = angular.module('app', ['ngResource']);
// token handler
app.factory('TokenHandler', function() { /* ... */ });
// inject the TokenHandler
app.factory('Todo', function($resource, TokenHandler) {
// get the token
var token = TokenHandler.get();
// and add it as a default param
return $resource('http://localhost:port/todos.json', {
port: ':3001',
access_token : token
});
})
You can call Todo.query() and it will append ?token=none to your URL. Or if you prefer to add a token placeholder you can of course do that too:
http://localhost:port/todos.json/:token
Hope this helps :)
Following your accepted answer, I would propose to extend the resource in order to set the token with the Todo object:
.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
var resource = $resource('http://localhost:port/todos/:id', {
port:":3001",
id:'#id'
}, {
update: {method: 'PUT'}
});
resource = tokenHandler.wrapActions( resource, ["query", "update"] );
resource.prototype.setToken = function setTodoToken(newToken) {
tokenHandler.set(newToken);
};
return resource;
}]);
In that way there is no need to import the TokenHandler each time you want to use the Todo object and you can use:
todo.setToken(theNewToken);
Another change I would do is to allow default actions if they are empty in wrapActions:
if (!actions || actions.length === 0) {
actions = [];
for (i in resource) {
if (i !== 'bind') {
actions.push(i);
}
}
}

Categories

Resources