node.js(Javascript) google-api-nodejs-client - list messages - google-api

My question was voted down, so i am rewriting it hopefully this is more succinct
I am stuck at writing a javascript function to list messages in inbox.
Using - official "google-api-nodejs-client", node.js, electron, (and javascript)
Goal: list messages in gmail inbox
For that to work i need to authorize first then ask for the messages
Authorize
- I copied the code from google node.js quickstart
- this works in electron (well actually node.js as it is a command line script).
Ask for Messages
- Google has an example, i copied it adjusted some parts but doesn't work. I think the example i am working from is not designed for the node.js "google-api-nodejs-client".
- Maybe it needs a different authorize
This is the listmessages function from the google example, i can't seem to figure out how to make this work with the authorize from list labels. This is what i have tried
changing gapi to google
changing userId to 'me'
changing givig it a query
Does not use a client library.
Does not use a client library.
/**
* Retrieve Messages in user's mailbox matching query.
*
* #param {String} userId User's email address. The special value 'me'
* can be used to indicate the authenticated user.
* #param {String} query String used to filter the Messages listed.
* #param {Function} callback Function to call when the request is complete.
*/
function listMessages(userId, query, callback) {
var getPageOfMessages = function(request, result) {
request.execute(function(resp) {
result = result.concat(resp.messages);
var nextPageToken = resp.nextPageToken;
if (nextPageToken) {
request = gapi.client.gmail.users.messages.list({
'userId': userId,
'pageToken': nextPageToken,
'q': query
});
getPageOfMessages(request, result);
} else {
callback(result);
}
});
};
var initialRequest = gapi.client.gmail.users.messages.list({
'userId': userId,
'q': query
});
getPageOfMessages(initialRequest, []);
}
This is the aurhorize function that works to list labels.
var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'gmail-nodejs-quickstart.json';
// Load client secrets from a local file.
fs.readFile('client_secret.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the
// Gmail API.
authorize(JSON.parse(content), listLabels);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
var clientSecret = credentials.installed.client_secret;
var clientId = credentials.installed.client_id;
var redirectUrl = credentials.installed.redirect_uris[0];
var auth = new googleAuth();
var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function(err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
*
* #param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback to call with the authorized
* client.
*/
function getNewToken(oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
console.log('Authorize this app by visiting this url: ', authUrl);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Enter the code from that page here: ', function(code) {
rl.close();
oauth2Client.getToken(code, function(err, token) {
if (err) {
console.log('Error while trying to retrieve access token', err);
return;
}
oauth2Client.credentials = token;
storeToken(token);
callback(oauth2Client);
});
});
}
/**
* Store token to disk be used in later program executions.
*
* #param {Object} token The token to store to disk.
*/
function storeToken(token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code != 'EEXIST') {
throw err;
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token));
console.log('Token stored to ' + TOKEN_PATH);
}
/**
* Lists the labels in the user's account.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function listLabels(auth) {
var gmail = google.gmail('v1');
gmail.users.labels.list({
auth: auth,
userId: 'me',
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var labels = response.labels;
if (labels.length == 0) {
console.log('No labels found.');
} else {
console.log('Labels:');
for (var i = 0; i < labels.length; i++) {
var label = labels[i];
console.log('- %s', label.name);
}
}
});
}

The flow of your program should look like this:
Then you can use the api to list messages. (just to get you started):
export function listMessages(oauth2Client, userId, query, callback) {
const gmail = google.gmail('v1');
gmail.users.messages.list({
auth: oauth2Client,
userId: 'me',
}, (err, response) => {
console.log(response);
});
}
Notice the oauth2Client parameter. This is the object you get from your function authorize. Let me know if you have any questions.

Related

Configuring Custom Challenge to do token swap in AWS Cognito

How do you properly configure custom challenge by sending an access_token from
cookie without a session?
const {
CognitoUserPool,
AuthenticationDetails,
CognitoUser
} = require('amazon-cognito-identity-js');
async function asyncAuthenticateUser(cognitoUser, cognitoAuthenticationDetails) {
return new Promise(function (resolve, reject) {
cognitoUser.initiateAuth(cognitoAuthenticationDetails, {
onSuccess: resolve,
onFailure: reject,
customChallenge: resolve
})
})
}
async function asyncCustomChallengeAnswer(cognitoUser, challengeResponse) {
return new Promise(function (resolve, reject) {
cognitoUser.sendCustomChallengeAnswer(challengeResponse, {
onSuccess: resolve,
onFailure: reject,
customChallenge: reject // We do not expect a second challenge
}
})
}
// omitted part of codes for brevity...
// We have tokens as cookie already that means a successful login previously succeeded
// but this login has probably been done from a different client with a different client_id
// We call the custom auth flow along with the token we have to get a new one for the current client_id
// For this to work we need to extract the username from the cookie
let tokenDecoded = jwt_decode(cookies.access_token);
let tokenUsername = tokenDecoded['username'];
var authenticationData = {
Username: tokenUsername,
AuthParameters: {
Username: tokenUsername,
}
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var poolData = {
UserPoolId: process.env.AUTH_AMPLIFYIDENTITYBROKERAUTH_USERPOOLID,
ClientId: client_id
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var userData = {
Username: tokenUsername,
Pool: userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.setAuthenticationFlowType("CUSTOM_AUTH");
try {
// Initiate the custom flow
await asyncAuthenticateUser(cognitoUser, authenticationDetails);
// Answer the custom challenge by providing the token
var result = await asyncCustomChallengeAnswer(cognitoUser, cookies.access_token);
var encrypted_id_token = await encryptToken(result.getIdToken().getJwtToken());
var encrypted_access_token = await encryptToken(result.getAccessToken().getJwtToken());
var encrypted_refresh_token = await encryptToken(result.getRefreshToken().getToken());
params.Item.id_token = encrypted_id_token;
params.Item.access_token = encrypted_access_token;
params.Item.refresh_token = encrypted_refresh_token;
}
catch (error) {
console.log("Token swap fail, this may be a tentative of token stealing");
return { // Redirect to login page with forced pre-logout
statusCode: 302,
headers: {
Location: '/?client_id=' + client_id + '&redirect_uri=' + redirect_uri + '&authorization_code=' + authorizationCode + '&forceAuth=true' + insertStateIfAny(event),
}
};
}
I have this part of codes where it invoke the iniateAuth then send a custom challenge answer.
// Initiate the custom flow
await asyncAuthenticateUser(cognitoUser, authenticationDetails);
// Answer the custom challenge by providing the token
var result = await asyncCustomChallengeAnswer(cognitoUser, cookies.access_token);
This complains due to the request session being empty.
The below are the auth challenge for create.
function handler(event, context, callback) {
// This function does nothing, the challenge do not need to be prepared
// Verify challenge will just verify the token provided
event.response.publicChallengeParameters = {};
event.response.publicChallengeParameters.question = 'JustGimmeTheToken';
event.response.privateChallengeParameters = {};
event.response.privateChallengeParameters.answer = 'unknown';
event.response.challengeMetadata = 'TOKEN_CHALLENGE';
event.response.challengeResult = true;
callback(null, event);
}
This is for the define challenge.
function handler(event, context, callback) {
// This function define the sequence to obtain a valid token from a valid token of another client
if (event.request.session.length == 0) {
// This is the first invocation, we ask for a custom challenge (providing a valid token)
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'CUSTOM_CHALLENGE';
} else if (
event.request.session.length == 1 &&
event.request.session[0].challengeName == 'CUSTOM_CHALLENGE' &&
event.request.session[0].challengeResult == true
) {
// The custom challenge has been answered we retrun the token
event.response.issueTokens = true;
event.response.failAuthentication = false;
}
context.done(null, event);
}
It goes both in this auth challenge but never goes to the verify challenge where
the is the below
function handler(event, context, callback) {
const params = {
AccessToken: event.request.challengeAnswer
};
const userInfo = await cognito.getUser(params).promise();
if (userInfo.Username === event.username) {
event.response.answerCorrect = true;
} else {
// Someone tried to get a token of someone else
event.response.answerCorrect = false;
}
callback(null, event);
}

How to create a shared drive with Google Drive API (node.js)

I'm trying to make a script that makes a shared drive from the google drive api but right now I'm getting an error inside the createSharedDrive(auth) function where it says
drive.drives.create
as it's saying the 'create' function is undefined. Not sure how to fix it. The google drive api documentation doesn't show the entire script to create a shared drive.
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
const { uuid } = require('uuidv4');
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/drive'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';
// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Drive API.
//authorize(JSON.parse(content), listFiles);
authorize(JSON.parse(content), createSharedDrive);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
const {client_secret, client_id, redirect_uris} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getAccessToken(oAuth2Client, callback);
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
* #param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback for the authorized client.
*/
function getAccessToken(oAuth2Client, callback) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
function createSharedDrive(auth){
const drive = google.drive({version: 'v3', auth});
var driveMetadata = {
'name': 'Testing Create Shared Drive'
};
var requestId = uuid;
drive.drives.create({
resource: driveMetadata,
requestId: requestId,
fields: 'id'
}, function (err, drive) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('Drive Id: ', drive.id);
}
});
}
The create method does not contains any "resource" property. It contains a requestId property and a requestBody property; Therefore, your request should look like this:
function createSharedDrive(auth){
const drive = google.drive({version: 'v3', auth});
var driveMetadata = {
'name': 'Testing Create Shared Drive'
};
var requestId = uuid;
drive.drives.create({
requestBody: driveMetadata,
requestId: requestId,
fields: 'id'
}, function (err, drive) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('Drive Id: ', drive.id);
}
});
}

How to publish to iot stream from lambda

I keep having a timeout in my lambda function when I try to call the iotData publish function. Code below. It always times out without error. This function works from the sam local command line. I imagine this is a permissions error with lambda. The strange thing is I've given permissions for IoT, Kinesis and SNS already to this lambda function but nothing is working.
'use strict';
console.log('Loading function');
require('dotenv').config();
const {Pool} = require('pg');
const pool = new Pool();
const AWS = require('aws-sdk');
console.log("finished loading");
/** * Provide an event that contains the following keys:
* * * - resource: API Gateway resource for event
* * - path: path of the HTTPS request to the microservices API call
* * - httpMethod: HTTP method of the HTTPS request from microservices API call
* * - headers: HTTP headers for the HTTPS request from microservices API call
* * - queryStringParameters: query parameters of the HTTPS request from microservices API call
* * - pathParameters: path parameters of the HTTPS request from microservices API call
* * - stageVariables: API Gateway stage variables, if applicable
* * - body: body of the HTTPS request from the microservices API call
* */
exports.handler = function(event, context, callback) {
console.log("starting");
let _response = "";
context.callbackWaitsForEmptyEventLoop = false;
if(event.httpMethod==="POST" && event.resource==="/pings"){
var body = JSON.parse(event.body);
console.log("here2");
pool.query("SELECT name from pings where test = decode($1,'hex');",[body.bid], (err,res)=>{
if(err){
console.error(err.stack);
_response = buildOutput(500, {
message:"error in pg"
});
callback(_response, null);
}
console.log("here3");
var iotdata = new AWS.IotData({endpoint:'XXXXXXX.iot.us-east-1.amazonaws.com'});
const publishParams = {
topic: body.topic,
payload: Buffer.from(JSON.stringify({
message: "Welcome to "+res.rows[0].name+" house"
}), 'utf8'),
qos: 0
}
console.log("here4");
iotdata.publish(publishParams, function(err, data) {
if(err){
console.error(err.stack);
_response = buildOutput(500, {
message:"error in pg"
});
callback(_response,null);
}
_response = buildOutput(200, {message: "success"});
callback(null, _response);
});
});
} else {
_response = buildOutput(500, {
message:"path not found"
});
callback(_response,null);
}
};
/* Utility function to build HTTP response for the microservices output */
function buildOutput(statusCode, data) {
let _response = {
statusCode: statusCode,
headers: {
"Access-Control-Allow-Origin": "*"
},
body: JSON.stringify(data)
};
return _response;
}
policy
{
"Sid": "",
"Effect": "Allow",
"Action": [
"iot:*"
],
"Resource": "*"
},
UPDATE:
I attempted to give the lambda function admin access temporarily and that did not even work.
This is how you can post on MQTT topic directly from lambda function.
Use the code written in Node.js 10.x
var AWS = require('aws-sdk');
const IOT_ENDPOINT = "yourIoTentPoint.iot.region.amazonaws.com"
var iotdata = new AWS.IotData({endpoint:IOT_ENDPOINT});
exports.handler = function(event, context, callback) {
var params = {
topic: 'my/topic',
payload: 'This is my menssage from Lambda function.',
qos: 1
};
iotdata.publish(params, function(err, data){
if(err){
callback(err, null);
}
else{
callback(null, {"published_message": params.payload, "topic": params.topic});
}
});
};
Also, you can check the message sent by subscribing on the topic my/topic through IoT Core>Test on AWS Console.

Code from beforeEach running after the 'it'spec

I'm using mocha and supertest to test a REST service. In this case, I need to test that a subsequent login after a GET request returns the query from that GET request.
login user
perform GET request on endpoint(s) using params
login user and expect params from GET request as a response.
Since the above test is going to be performed several times for various endpoints, this is what I have come up with:
/*
* getRequest.js
*/
'use strict';
var request = require('supertest')
, verifyUrl = require('./verifyUrl') // verifies basic url formatting
;
module.exports = getRequest;
/**
* Perform GET request using given parameters and call next when finished.
* #param getEnvironment {json}
* #param getEnvironment.url {string}
* #param getEnvironment.endPoint {string}
* #param getEnvironment.authorization {string}
* #param next {function} The callback
*/
function(getEnvironment, getParams, next) {
var url = verifyUrl(getEnvironment.url);
request(url)
.get(getEnvironment.endPoint)
.set("authorization", getEnvironment.authorization)
.set('Accept', 'application/json')
.query(getParams)
.end(next)
}
/*
* loginWithCallback.js
*
* Performs a basic login but depends on the callback to test the results of the login.
*/
'use strict';
var request = require('supertest')
, VerifyUrl = require('./VerifyUrl')
;
module.exports = LoginWithCallback;
/**
* Perform basic login, then call next passing in err and res for testing.
*
* #param setEnvironment {json} Expects url and authorization. Any other parameters will be ignored.
* #param setEnvironment.url {string} The url being tested.
* #param setEnvironment.authorization {string}
* #param next {function} callback function that will perform the actual test.
*/
function LoginWithCallback(setEnvironment, next) {
var url = VerifyUrl(setEnvironment.url);
request(url)
.post('api/users/login')
.set('authorization', setEnvironment.authorization)
.set('Accept', 'application/json')
.end(next);
}
/*
* e2eTest.js
*/
'use strict';
var base64_encode = require('Base64').btoa
, should = require('should')
, jsonValidator = require('is-my-json-valid')
, mergeJSON = require('lodash/merge')
, lodashClone = require('lodash/clone')
, responseSchema = require('./responseSchemas/200.json')
, login = require('./loginWithCallback')
, getRequest = require('./getRequest')
;
var username = "newUser" + Date.now()
, password = "passW0rd"
, testEnvironment = {
"url": "http://localhost:9000/",
"endPoint": "api/users/login",
"authorization": "Basic " + base64_encode(username + ":" + password)
}
;
var expectedResult = {};
describe('End to End testing on' + JSON.stringify(testEnvironment), function () {
describe('new user, ' + username + ', login', function () {
it('should return 200 and an empty body message.', function (done) {
login(testEnvironment, function (err, res) {
if (err) {
done(err);
}
res.statusCode.should.deepEqual(200);
var jsonValidate = jsonValidator(responseSchema, {verbose: true});
jsonValidate(res.body).should.be.true("Response body failed schema check: \n" +
JSON.stringify(jsonValidate.errors, null, 4));
res.body.should.deepEqual(expectedResult);
done();
});
});
});
describe('user, ' + username + ', logs in after performing get request', function () {
var getRequestParams = {"firstName":"john", "lastName":"doe"};
beforeEach(function() {
var getEnviron = lodashClone(testEnvironment);
getEnviron.endPoint = 'api/persons/findName';
mergeJSON(expectedResult, {"persons": {"findName": getRequestParams}});
getRequest(getEnviron, getReqestParams, function(err, res) {
if (err) {
done(err);
}
console.log("GET request: " JSON.stringify(res, null, 2));
done();
});
});
it('should return 200 and query values', function(done) {
login(testEnvironment, function (err, res) {
if (err) {
done(err)
}
console.log("it test: " + JSON.stringify(res, null, 2));
res.statusCode.should.deepEqual(200);
var jsonValidate = jsonValidator(responseSchema, {verbose: true});
jsonValidate(res.body).should.be.true("Response body failed schema check: \n" + JSON.stringify(jsonValidate.errors, null, 4));
res.body.should.deepEqual(expectedResult);
done();
});
});
});
What the above three files are supposed to do is the following:
1. create a new user
2. login new user and test its response for no previous query params. (passes)
3. perform the GET request and then print the result in the before block
4. perform the login of the user print and test results in the it block.
But what I'm getting is something like:
End to End testing on{"url":"http://localhost:9000/","endPoint":"api/users/login","authorization":"Basic bmV3VXNlcjE0NjY0NDI0OTEzNDc6cGFzc1cwcmQ="}
new user, newUser1466442491347, login
✓ should return 200 and an empty body message. (54ms)
user, newUser1466442491347, logs in after performing persons/findByName request
it test: [res text]
1) should return and query values
GET request: [res text]
1 passing
1 failing
Uncaught AssertionError: expected Object {} to equal Object {
persons: Object { findByName: Object { firstName: 'joe', lastName: 'jones' } }
}
As can be seen, the 'it' block is appearing to run before the before block has finished. From my reading of mocha, this should not happen as it waits for the before and beforeEach finishes before running the 'it' block. But, perhaps, the callback for the getRequest gets queued after the it block?
What am I doing wrong or misunderstanding?
You definitely must set your beforeEach hook so that Mocha knows it is asynchronous: either you return a promise to Mocha or you use the done callback. In order to use the done callback you must declare a callback that takes done:
beforeEach(function (done) {
(You could call it anything you want (e.g. finished), so long as you are consistent (call finished() later).)

Is there a soundcloud artwork url scheme like in the facebook api?

I'd like to display the artwork image of a soundcloud track I have the Track-URL from.
I know that when I extract the track key/id from the URL, I can request the artwork URL via their API and then inject the retrieved URL into a image tag.
What I'd like to know is if its possible to use some kind of URL schema to make soundcloud forward browsers to the correct artwork URL like its possible with facebook profile images.
For example:
Mark Zuckerbergs current profile picture has the URL http://profile.ak.fbcdn.net/hprofile-ak-prn2/t5/202896_4_1782288297_q.jpg
Thats some cryptic stuff because its hosted on a CDN. Soundcloud artwork URLs look pretty much cryptic as well.
Now, when I know marks facebook id/key ("zuck"), I can simply access his profile image like so:
http://graph.facebook.com/zuck/picture
That URL is automatically forwarded to the profile picture URL by the facebook API.
Using this URL schema you abstract away not only the reason for a additional API request, but they also safe processing time on their side.
Is there some functionality like this for soundcloud track artworks?
I wrote an express app that redirects to largest available image on their CDN, given artwork_url.
FixSoundCloudArtworkUrl.js
It uses their naming scheme and enumerates sizes one by one until some image returns status 200.
Source:
'use strict';
var express = require('express'),
app = express();
require('./config/development')(app, express);
require('./config/production')(app, express);
var redis = require('redis'),
request = require('request'),
Promise = require('bluebird');
Promise.promisifyAll(redis.RedisClient.prototype);
var redisSettings = app.set('redis'),
redisClient = redis.createClient(redisSettings.port, redisSettings.host, redisSettings.options);
app.configure(function () {
app.use(express.bodyParser());
app.use(app.router);
});
function sendError(res, status, error) {
if (!(error instanceof Error)) {
error = new Error(JSON.stringify(error));
}
return res
.status(status || 500)
.end(error && error.message || 'Internal Server Error');
}
function generateCacheHeaders() {
var maxAge = 3600 * 24 * 365;
return {
'Cache-Control': 'public,max-age=' + maxAge,
'Expires': new Date(Date.now() + (maxAge * 1000)).toUTCString()
};
}
function getCacheKey(url) {
return 'soundcloud-thumbnail-proxy:' + url;
}
app.get('/*', function (req, res) {
var originalUrl = req.params[0],
cacheKey = getCacheKey(originalUrl),
urls;
// https://developers.soundcloud.com/docs/api/reference#artwork_url
// This is a ridiculous naming scheme, by the way.
urls = [
originalUrl,
originalUrl.replace('-large', '-t500x500'),
originalUrl.replace('-large', '-crop'), // 400x400
originalUrl.replace('-large', '-t300x300'),
originalUrl.replace('-large', '-large') // 100x100
];
return redisClient.getAsync(cacheKey).then(function (cachedUrl) {
if (cachedUrl) {
return cachedUrl;
}
return Promise.reduce(urls, function (resolvedUrl, url) {
if (resolvedUrl) {
return resolvedUrl;
}
return new Promise(function (resolve) {
request.head(url, function (err, response) {
if (!err && response.statusCode === 200) {
resolve(url);
} else {
resolve(null);
}
});
});
}, null);
}).then(function (url) {
if (!url) {
throw new Error('File not found');
}
var headers = generateCacheHeaders();
for (var key in headers) {
if (headers.hasOwnProperty(key)) {
res.setHeader(key, headers[key]);
}
}
res.redirect(url);
redisClient.set(cacheKey, url);
redisClient.expire(cacheKey, 60 * 60 * 24 * 30);
}).catch(function (err) {
sendError(res, 404, err);
});
});
app.get('/crossdomain.xml', function (req, res) {
req.setEncoding('utf8');
res.writeHead(200, { 'Content-Type': 'text/xml' });
res.end('<?xml version="1.0" ?><cross-domain-policy><allow-access-from domain="*" /></cross-domain-policy>');
});
redisClient.on('ready', function () {
app.listen(app.set('port'));
});
redisClient.on('error', function () {
throw new Error('Could not connect to Redis');
});
module.exports = app;
No. The only documented way is: API Reference for "/tracks"

Resources