diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1e673132 --- /dev/null +++ b/.gitignore @@ -0,0 +1,63 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +.idea diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..0e40fe8f --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ + +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/.idea/fp.iml b/.idea/fp.iml new file mode 100644 index 00000000..24c9ae71 --- /dev/null +++ b/.idea/fp.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 00000000..d6e0fa02 --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..28a804d8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..50d453c2 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Proposal.md b/Proposal.md new file mode 100644 index 00000000..675ee793 --- /dev/null +++ b/Proposal.md @@ -0,0 +1,3 @@ +Randy Agudelo, René Borner, Caitlin Enright, Elie Hess final project proposal: + +For our final project, we will create a music-based social media application based on sharing music with others. Our website will have a global “feed” (similar to most social media sites), which will allow users to view songs recommended by others, listen to those songs, and recommend songs to the global community. Additionally, users will be able to rate songs from 1 to 5, and each song will display its average rating along with other information about the song. Finally, we will integrate with Spotify to play songs on the site. While a song is playing, the background of the site will display a simple audio visualization of the currently playing song. diff --git a/README.md b/README.md index 39e2788d..7df226cb 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,61 @@ -# cs4241-FinalProject - -For your final project, you'll implement a course project that exhibits your mastery of the course materials. -Similar to A4, this project gives you an opportunity to be creative and to pursue individual research and learning. - -## General description - -Your project should consist of a complete Web application, exhibiting facets of the three main sections of the course material: - -- Static Web page content and design. You should have a project that is accessible, easily navigable, and features significant content. -- Dynamic behavior implemented with JavaScript. -- Server-side programming *using Node.js*. Typically this will take the form of some sort of persistent data, authentication, and possibly server-side computation. - -Additionally, you should incorporate features that you independently research, design, and implement for your project. - -## Project ideation - -Excellent projects serve someone/some group; for this assignment you need to define your users and stakeholders. I encourage you to identify projects that will have impact, either artistically, politically, or in terms of productivity. Consider creating something useful for a cause or hobby you care about. - -## Logistics - -### Team size -Students are encouraged to work in teams of 2-5 students for the project. This will allow you to build a good project without expending an excessive amount of effort. While I would expect a team of four or five students to produce a project with more features, I expect a every team's work to exhibit all of the required facets described above. - -### Deliverables - -__Proposal:__ -Provide an outline of your project direction and the names of the team members. -The outline should have enough detail so that staff can determine if it meets the minimum expectations, or if it goes too far to be reasonable by the deadline. -This file must be named proposal.md so we can find it. -Submit a PR to turn it in by Monday, September 30th, before class - -There are no other scheduled checkpoints for your project. -You must be done in time to present before the final project demo day (October 10th). - -#### Turning in Your Outline / Project - -**NOTE: code is due before the project presentation day due to the end of term / grading schedule constraints** -Submit a second PR on the final project repo to turn in your app and code. - -Deploy your app, in the form of a webpage, to Glitch/Heroku/Digital Ocean or some other service. -Folks on the same team do not need to post the same webpage, but must instead clearly state who is on the team in their proposal. -(Staff will use the proposal to build the grading sheet.) - -## Final Presentation - -Presentations will occur during the final day of class. - -## FAQs - -- **Can I use XYZ framework?** You can use any web-based frameworks or tools available, but for your server programming you need to use node.js. +# cs4241-FinalProject - Music App +Team Members: Randy Agudelo, René Borner, Caitlin Enright, Elie Hess + +## 1. A brief description of what you created, and a link to the project itself. + +Glitch: https://cs4241-best-group-fp-7.glitch.me/
+Youtube video: https://youtu.be/rDX6Nd-vVTE
+ +We created a song recomendation sharing applications. Each user has their own account. When they sign in they see a feed of all song recomendations made. Song recomendations include the user who made it, the song name and artist, a comment about the song and a rating from 0-5 stars. Users can also add their on rating to the feed by used the bar on the left side of the page. They can search for any song on spotify using the seach bar and add their comment and rating. There is a song player at the top of the feed that allows the currently playing song to be paused or played. It also displays the album cover. When the song is playing there is a 3d vsualization in the background of the app that is in time with the current song. The album cover also spins like a record player. Any of the songs in the feed can be selected and then become the currently playing song. This allows users to hear the songs that they are reading recomentdations about. + +## 2. Any additional instructions that might be needed to fully use your project (login information etc.) +In order to see the animation and hear the songs playing, you need to authorize a spotify premium account. + + +## 3. An outline of the technologies you used and how you used them. +- Spotify API’s: we used these API’s to allow the songs to be played through the app. It also let the user search for a certain track using the name. Information given by the api about the track’s beats and tempo helped to create the visualization in the background. +- Bootstrap: we used this for all of the layout and styling of the page to give the app a more cohesive look +- Three.js: This was used to create the 3-d background. The visual moves along with the beats of the currently playing song +- MongoDB: This was used for the database which stores the user login information as well as all of the recommendations (maybe write about password hashing & salting with bcrypt) +- jQuery: JQuery was used to help with onclick functionality and passing data + +## 4. What challenges you faced in completing the project. +The spotify api does not return an mp3 file so making the 3d visualizations based off of numerical data was difficult +We had problems working with the spotify Web API because of the authorization process and the many possible routes etc +Certain bootstrap elements had preset values that were confusing to understand and difficult to work around. + +## 5. What each group member was responsible for designing / developing. + +Caitlin Enright: +- Created basic homepage layout +- Worked on search functionality and interactable table +- Created recommendation form +- Made UI changes about color, layout, spacing +- Helped set spotify player to current track +- Play/Pause button changes + +Elie Hess +- Created main recommendation feed +- Made client-side functions to add and retrieve recommendations to/from MongoDB +- Created the current star rating system +- Tweaked UI to look cleaner +- Worked on code cleanup, optimization +- Managed Git repo + +Randy Agudelo: +- Registered Spotify application to be able to work with the premium Spotify users +- Initialized the server +- Worked on the server and backend to authorize the user through Spotify to gain access to their account +- Created different paths on the server to get specific Spotify Info +- Created an audio visualizer in the background +- Helped with some of the error handling + +René Borner: +- MongoDB setup with authentification and recommendation functionality and backend routes +- Backend and frontend connection for different functionalities like getting the spotify token in the frontend +- Round and turning song cover image +- Worked on spotify player with track number +- Worked on search functionality +- Worked on star rating + + diff --git a/app.js b/app.js new file mode 100644 index 00000000..3868eaf2 --- /dev/null +++ b/app.js @@ -0,0 +1,354 @@ +const express = require('express'), + path = require('path'), + cookieParser = require('cookie-parser'), + logger = require('morgan'), + session = require('express-session'), + request = require('request'), + cors = require('cors'), + querystring = require('querystring'), + mongo = require('mongodb').MongoClient, + bcrypt = require('bcrypt'), + url = "mongodb+srv://root:admin@cluster0-qdoiu.azure.mongodb.net/test?retryWrites=true&w=majority", + passport = require('passport'), + LocalStrategy = require('passport-local').Strategy, + app = express(), + favicon = require('serve-favicon'), + client_id = 'f250b3f0ce604150b056707b8e25a328', + client_secret = 'c26768e1850047ceba186a08bb061a9d', //this is VERY IMPORTANT and should NEVER be revealed in public + redirect_uri = 'http://localhost:3000/callback', //redirects to this when authorization passes or fails + scopes = 'user-read-private user-read-email streaming app-remote-control', + stateKey = 'spotify_auth_state' + +let currentUser = [], + access_token = null, + refresh_token = null, + product = null, + track_id = '3n3Ppam7vgaVa1iaRUc9Lp', + audioAnalysis; + +app.use(favicon(__dirname + '/public/images/favicon.ico')) +app.use(logger('dev')) +app.use(express.json()) +app.use(express.urlencoded({extended: false})) +app.use(cookieParser()) +app.use(cors()) +app.use(express.static(path.join(__dirname, 'public'))) + +app.use(session({ + secret: "tHiSiSasEcRetStr", + resave: false, + saveUninitialized: false +})) + +app.use(passport.initialize()) +app.use(passport.session()) + +passport.serializeUser((user, done) => done(null, currentUser[0].username)) + +passport.deserializeUser((username, done) => { + if (currentUser[0] !== undefined) { + done(null, currentUser[0]) + } else { + done(null, false, {message: 'user not found; session not restored'}) + } +}) +passport.use('local-login', new LocalStrategy( + function (username, password, done) { + new Promise(function (resolve, reject) { + mongo.connect(url, { + useNewUrlParser: true, + useUnifiedTopology: true + }, (err, client) => { + if (err) { + reject(err) + } + const db = client.db('MusicApp') + const collection = db.collection('user') + collection.find({"username": username}).toArray((err, items) => { + resolve(items) + }) + }) + }).then(function (result) { + if (typeof result[0] == 'undefined') { + return done(null, false, {"message": "Wrong username"}) + } else { + currentUser = result + bcrypt.compare(password, result[0].password, function (err, res) { + if (res) { + return done(null, result[0]) + } else { + return done(null, false, {"message": "Password incorrect"}) + } + }) + } + }, function (err) { + return done(null, false, {"message": "User not found."}) + }) + }) +) + +const generateRandomString = function (length) { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + for (let i = 0; i < length; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +}; + +app.get("/", isLoggedIn, function (req, res) { + res.sendFile(__dirname + "/public/home.html") +}) +app.get("/login", function (req, res) { + res.sendFile(__dirname + "/public/login.html") +}) + +app.get("/spotifyAccess", function (req, res) { + let state = generateRandomString(16); + res.cookie(stateKey, state); + res.redirect('https://accounts.spotify.com/authorize?' + + querystring.stringify({ + response_type: 'code', + client_id: client_id, + scope: scopes, + redirect_uri: redirect_uri, + state: state + })) +}) + +app.get('/callback', function (req, res) { + + // your application requests refresh and access tokens + // after checking the state parameter + + let code = req.query.code || null; + let state = req.query.state || null; + let storedState = req.cookies ? req.cookies[stateKey] : null; + + if (state === null || state !== storedState) { + res.redirect('/#' + + querystring.stringify({ + error: 'state_mismatch' + })); + } else { + res.clearCookie(stateKey); + const authOptions = { + url: 'https://accounts.spotify.com/api/token', + form: { + code: code, + redirect_uri: redirect_uri, + grant_type: 'authorization_code' + }, + headers: { + 'Authorization': 'Basic ' + (Buffer.from(client_id + ':' + client_secret).toString('base64')) + }, + json: true + }; + + request.post(authOptions, function (error, response, body) { + if (!error && response.statusCode === 200) { + + access_token = body.access_token + refresh_token = body.refresh_token; + + const options = { + url: 'https://api.spotify.com/v1/me', + headers: {'Authorization': 'Bearer ' + access_token}, + json: true + }; + + // use the access token to access the Spotify Web API + request.get(options, function (error, response, body) { + product = body.product + }); + + // we can also pass the token to the browser to make requests from there + res.redirect('/#' + + querystring.stringify({ + access_token: access_token, + refresh_token: refresh_token + })) + } else { + res.redirect('/#' + + querystring.stringify({ + error: 'invalid_token' + })); + } + }); + } +}); +app.post('/currentTrack', function (req, res) { + let parsedData = req.body + track_id = parsedData.track +}) + +app.get('/trackAnalysis', function (req, res) { + const options = { + url: 'https://api.spotify.com/v1/audio-analysis/' + track_id, //req.body + headers: {'Authorization': 'Bearer ' + access_token}, + json: true + }; + request.get(options, function (response, body) { + res.send(body) + + }) +}) + + +app.get('/refresh_token', function (req, res) { + + // requesting access token from refresh token + let refresh_token = req.query.refresh_token; + let authOptions = { + url: 'https://accounts.spotify.com/api/token', + headers: {'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))}, + form: { + grant_type: 'refresh_token', + refresh_token: refresh_token + }, + json: true + }; + + request.post(authOptions, function (error, response, body) { + if (!error && response.statusCode === 200) { + access_token = body.access_token; + res.send({ + 'access_token': access_token + }); + } + }); +}); + +app.get("/user", function (req, res) { + const json = { + user: currentUser[0] + } + res.send(JSON.stringify(json)) +}) + +app.get("/token", function (req, res) { + const answerObj = {} + answerObj.name = "token" + answerObj.name = "product" + answerObj.product = product + answerObj.token = access_token + res.send(JSON.stringify(answerObj)) +}) + +app.get("/register", function (req, res) { + res.sendFile(__dirname + "/public/register.html") +}) + +app.get("/logout", function (req, res) { + req.logout() + res.redirect("/") +}) + +app.get("/recommendation", function (req, res) { + new Promise(function (resolve, reject) { + mongo.connect(url, { + useNewUrlParser: true, + useUnifiedTopology: true + }, (err, client) => { + if (err) { + reject(err) + } + const db = client.db('MusicApp') + const collection = db.collection('recommendations') + collection.find({}).toArray().then(function (arr) { + let entries = [] + arr.forEach(function (element) { + const jaysawn = JSON.parse(JSON.stringify(element)) + if (jaysawn.username == null) + console.log("null username") + else if (jaysawn.songid == null) + console.log("null songid") + else if (jaysawn.rating == null) + console.log("null rating") + else if (jaysawn.caption == null) + console.log("null caption") + else if (jaysawn.songname == null) + console.log("null songname") + else if (jaysawn.artist == null) + console.log("null artist") + else { + let entry = { + username: jaysawn.username, + songid: jaysawn.songid, + rating: jaysawn.rating, + caption: jaysawn.caption, + songname: jaysawn.songname, + artist: jaysawn.artist + } + entries.push(entry) + } + }) + res.send(JSON.stringify(entries)) + }) + }) + }) +}) + +app.post("/login", + passport.authenticate("local-login", {failureRedirect: "/"}), + function (req, res) { + res.redirect("/spotifyAccess") //redirects to spotify access page once sign in authorizes + } +) + +app.post("/register", function (req, res) { + bcrypt.hash(req.body.password, 10, function (err, hash) { + new Promise(function (resolve, reject) { + mongo.connect(url, { + useNewUrlParser: true, + useUnifiedTopology: true + }, (err, client) => { + if (err) { + reject(err) + } + const db = client.db('MusicApp') + const collection = db.collection('user') + if (req.body.username === "badUsernameZQFMGB") return + collection.insertOne({ + "username": req.body.username, + "password": hash + }).then(() => res.redirect("/")) + }) + }) + }) +}) + +app.post("/recommendation", function (req, res) { + new Promise(function (resolve, reject) { + mongo.connect(url, { + useNewUrlParser: true, + useUnifiedTopology: true + }, (err, client) => { + if (err) { + reject(err) + } + const db = client.db('MusicApp') + const collection = db.collection('recommendations') + collection.insertOne({ + "username": req.body.username, + "songid": req.body.songid, + "rating": req.body.rating, + "caption": req.body.caption, + "songname": req.body.songname, + "artist": req.body.artist + }).then(() => { + res.redirect("/"); + }) + }) + }) +}) + +function isLoggedIn(req, res, next) { + if (req.isAuthenticated()) + return next() + res.redirect("/login") +} + +app.listen(process.env.PORT || 3000) +module.exports = app diff --git a/bin/www b/bin/www new file mode 100644 index 00000000..810eb7aa --- /dev/null +++ b/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +const app = require('../app'); +const debug = require('debug')('fp:server'); +const http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +const port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +const server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + const port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + const bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + const addr = server.address(); + const bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..81247943 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1301 @@ +{ + "name": "fp", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "bcrypt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.6.tgz", + "integrity": "sha512-taA5bCTfXe7FUjKroKky9EXpdhkVvhE5owfxfLYodbrAR1Ul3juLmIQmIQBK4L9a5BuUcE6cqmwT+Da20lF9tg==", + "requires": { + "nan": "2.13.2", + "node-pre-gyp": "0.12.0" + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "bson": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", + "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-parser": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz", + "integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6" + } + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + } + } + }, + "express-session": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.16.2.tgz", + "integrity": "sha512-oy0sRsdw6n93E9wpCNWKRnSsxYnSDX9Dnr9mhZgqUEEorzcq5nshGYSZ4ZReHFhKQ80WI5iVUUSPW7u3GaKauw==", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.1.2", + "uid-safe": "~2.1.5" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.2.tgz", + "integrity": "sha512-EXyErtpHbn75ZTsOADsfx6J/FPo6/5cjev46PXrcTpd8z3BoRkXgYu9/JVqrI7tusjmwCZutGeRJeU0Wo1e4Cw==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.8.6.tgz", + "integrity": "sha512-lFG7d6g3+/UaFDCOtqPiKAC9zngWWsQZl1g5q6gaONqrjq61SX2xFqXMleQiFVyDpYwa018E9hmlAFY22PCb+A==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.2.tgz", + "integrity": "sha512-lsNFqSHdJ21EwKzCp12HHJGxSMtHkCW1EMA9cceG3MkMNARjuWotZnMe3NKNshAvFXpm4loZqmYsCmRwhS2JMw==", + "requires": { + "minipass": "^2.9.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mongodb": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.2.tgz", + "integrity": "sha512-fqJt3iywelk4yKu/lfwQg163Bjpo5zDKhXiohycvon4iQHbrfflSAz9AIlRE6496Pm/dQKQK5bMigdVo2s6gBg==", + "requires": { + "bson": "^1.1.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2" + } + }, + "morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==" + }, + "needle": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", + "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "node-pre-gyp": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz", + "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==" + }, + "npm-packlist": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz", + "integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "passport": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", + "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + } + }, + "passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "requires": { + "passport-strategy": "1.x.x" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serve-favicon": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", + "integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=", + "requires": { + "etag": "~1.8.1", + "fresh": "0.5.2", + "ms": "2.1.1", + "parseurl": "~1.3.2", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + } + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yallist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.0.tgz", + "integrity": "sha512-6gpP93MR+VOOehKbCPchro3wFZNSNmek8A2kbkOAZLIZAYx1KP/zAqwO0sOHi3xJEb+UBz8NaYt/17UNit1Q9w==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..d64ae82f --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "fp", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www --inspect" + }, + "dependencies": { + "bcrypt": "^3.0.6", + "cookie-parser": "~1.4.4", + "cors": "^2.8.5", + "debug": "~2.6.9", + "express": "~4.16.1", + "express-session": "^1.16.2", + "http-errors": "~1.6.3", + "mongodb": "^3.3.2", + "morgan": "~1.9.1", + "passport": "^0.4.0", + "passport-local": "latest", + "querystring": "^0.2.0", + "request": "^2.88.0", + "serve-favicon": "^2.5.0" + } +} diff --git a/public/error.html b/public/error.html new file mode 100644 index 00000000..b3828085 --- /dev/null +++ b/public/error.html @@ -0,0 +1,10 @@ + + + + + Music Application + + +

An error occurred

+ + \ No newline at end of file diff --git a/public/home.html b/public/home.html new file mode 100644 index 00000000..3a059247 --- /dev/null +++ b/public/home.html @@ -0,0 +1,133 @@ + + + + + + Music App + + + + + + + + + + + + + + Music App + + + +
+
+ +
+
+
+ +
+
+ +
+ +
+
+
+
+ + + +
+
+ + +
+
+ + + + + + + + + + +
+
+
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+
+ + +
+ + + + + + + + + + + diff --git a/public/images/emptystar.png b/public/images/emptystar.png new file mode 100644 index 00000000..e736b1c5 Binary files /dev/null and b/public/images/emptystar.png differ diff --git a/public/images/favicon.ico b/public/images/favicon.ico new file mode 100644 index 00000000..ab06a32f Binary files /dev/null and b/public/images/favicon.ico differ diff --git a/public/images/music.png b/public/images/music.png new file mode 100644 index 00000000..81f9f748 Binary files /dev/null and b/public/images/music.png differ diff --git a/public/images/pause.png b/public/images/pause.png new file mode 100644 index 00000000..383ebbd3 Binary files /dev/null and b/public/images/pause.png differ diff --git a/public/images/play.png b/public/images/play.png new file mode 100644 index 00000000..8b94fbc3 Binary files /dev/null and b/public/images/play.png differ diff --git a/public/images/purplestar.png b/public/images/purplestar.png new file mode 100644 index 00000000..2d2176a4 Binary files /dev/null and b/public/images/purplestar.png differ diff --git a/public/images/songnotfound.png b/public/images/songnotfound.png new file mode 100644 index 00000000..b477b7eb Binary files /dev/null and b/public/images/songnotfound.png differ diff --git a/public/js/audioVisualizer.js b/public/js/audioVisualizer.js new file mode 100644 index 00000000..d2cb0264 --- /dev/null +++ b/public/js/audioVisualizer.js @@ -0,0 +1,109 @@ +let timeout; + +function trackInfo() { + let analysis + let scene, camera, renderer, mesh1, mesh2, mesh3, mesh4; + let numArray = []; + let confidence = 1; + (async () => { + const rawResponse = await fetch('/trackAnalysis', { + method: 'GET' + }) + const audioAnalysis = await rawResponse.json() + analysis = audioAnalysis.body + let track = audioAnalysis.body.track + let beats = audioAnalysis.body.beats + + function randomNumber(min, max) { + return Math.floor(Math.random() * (max - min) + min); + } + + const randomNum = function () { + for (let i = 0; i < beats.length; i++) { + numArray.push(randomNumber(65, 93)) + } + } + randomNum() + + const init = function () { + scene = new THREE.Scene() + camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000) + + renderer = new THREE.WebGLRenderer({antialias: true}) + renderer.setSize(window.innerWidth, window.innerHeight) + + $(".background").append(renderer.domElement) + + const light = new THREE.PointLight("white", 1.5, 100) + light.position.set(10, 1, 10); + + scene.add(light) + + const light2 = new THREE.PointLight("white", 1.5, 100) + light2.position.set(35, 20, 50) + scene.add(light2); + + const ambLight = new THREE.AmbientLight("white", 0.3, 100) + ambLight.position.set(5, 3, 100) + scene.add(ambLight); + + const geometry = new THREE.IcosahedronGeometry(7); + const material = new THREE.MeshLambertMaterial({color: "#FE3B53"}) + + mesh1 = new THREE.Mesh(geometry, material) + mesh2 = new THREE.Mesh(geometry, material) + mesh3 = new THREE.Mesh(geometry, material) + mesh4 = new THREE.Mesh(geometry, material) + + mesh1.scale.set(3, 3, 3) + mesh2.scale.set(3, 3, 3) + mesh3.scale.set(3, 3, 3) + mesh4.scale.set(3, 3, 3) + + + const randomPosPerSec = () => { + for (let i = 0; i < beats.length; i++) { + timeout = setTimeout(() => { //stores previous number to go to next number + + mesh1.position.x = (beats[i].confidence) * numArray[i] * 1.7 + mesh2.position.y = (beats[i].confidence) * numArray[i] * 1.2 + mesh3.position.z = (beats[i].confidence) * numArray[i] * 1.7 + mesh4.position.y = -1 * (beats[i].confidence) * numArray[i] * 1.2 + }, i * ((1 / (track.tempo / 60) * 1000))) //beats per ms + } + } + + randomPosPerSec() + + scene.add(mesh1, mesh2, mesh3, mesh4) + + } + const animate = function () { + requestAnimationFrame(animate); + + mesh1.rotation.z += track.tempo_confidence / 10 + mesh2.rotation.y += track.tempo_confidence / 10 + mesh3.rotation.x += track.tempo_confidence / 10 + mesh4.rotation.z += track.tempo_confidence / 10 + + camera.position.x = 150 + camera.position.z = 150 + + camera.lookAt(scene.position); + + renderer.render(scene, camera); + } + const windowResize = function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight) + } + + window.addEventListener('resize', windowResize, false); + + init(); + animate(); + })() +} + +trackInfo() diff --git a/public/js/createPlayer.js b/public/js/createPlayer.js new file mode 100644 index 00000000..12135f6a --- /dev/null +++ b/public/js/createPlayer.js @@ -0,0 +1,144 @@ +const clientId = 'f250b3f0ce604150b056707b8e25a328' +const redirectUri = 'http://localhost:3000' +const scopes = [ + 'streaming', + 'user-read-birthdate', + 'user-read-private', + 'user-modify-playback-state' +] +let _token +let player +let playerData +let currentTrackID = "3n3Ppam7vgaVa1iaRUc9Lp" +let currentlyPlaying = true +let interval + +window.onSpotifyPlayerAPIReady = () => { + createPlayerForSong(currentTrackID) +} + +function createPlayerForSong(track) { + fetch('/token', { + method: 'GET' + }).then(response => { + return response.json().then(response => { + console.log(response.token) + _token = response.token + player = new Spotify.Player({ + name: 'Web Playback SDK Template', + getOAuthToken: cb => { + cb(_token) + } + }) + // Error handling + player.on('initialization_error', e => console.error(e)) + player.on('authentication_error', e => console.error(e)) + player.on('account_error', e => console.error(e)) + player.on('playback_error', e => console.error(e)) + + // Playback status updates + player.on('player_state_changed', state => { + if (state != null) { + $('#current-track').attr('src', state.track_window.current_track.album.images[0].url) + $('#current-track-name').text(state.track_window.current_track.name) + $('#current-track-artist').text(state.track_window.current_track.artists[0].name) + } + + }) + + // Ready + player.on('ready', data => { + playerData = data + // Play a track using our new device ID + getAudio(data.device_id) + }) + + // Connect to the player! + player.connect() + + // Play a specified track on the Web Playback SDK's device ID + function getAudio(device_id) { + $.ajax({ + url: "https://api.spotify.com/v1/me/player/play?device_id=" + device_id, + type: "PUT", + data: '{"uris": ["spotify:track:' + track + '"]}', + beforeSend: function (xhr) { + xhr.setRequestHeader('Authorization', 'Bearer ' + _token) + }, + success: function (data) { + /*console.log(data)*/ + } + }) + } + }) + } + ) +} + +function turningRecord(turn) { + let counter = 0 + if (turn) { + interval = setInterval(function () { + counter -= 1 + $("#current-track").css({ + MozTransform: 'rotate(-' + -counter + 'deg)', + WebkitTransform: 'rotate(' + -counter + 'deg)', + transform: 'rotate(' + -counter + 'deg)' + }) + + }, 10) + } else { + clearInterval(interval) + } +} + +turningRecord(currentlyPlaying) + +function togglePlayPause() { + currentlyPlaying = !currentlyPlaying + if (!currentlyPlaying) { + $("canvas").remove() + } else { + trackInfo() + } + turningRecord(currentlyPlaying) + player.togglePlay() + let playPauseButton = document.getElementById("play-pause-button-player"); + let imageSrc = playPauseButton.getAttribute('src') + if (imageSrc === "images/play.png") { + playPauseButton.setAttribute('src', "images/pause.png"); + } else if (imageSrc === "images/pause.png") { + playPauseButton.setAttribute('src', "images/play.png"); + } +} + +function playSomeTrackID(track) { + if (currentlyPlaying) { + let playPauseButton = document.getElementById("play-pause-button"); + playPauseButton.setAttribute('src', "images/pause.png"); + } + + updateCurrentTrack(track) + player.disconnect() + createPlayerForSong(currentTrackID) + if (!currentlyPlaying) { + currentlyPlaying = true + turningRecord(currentlyPlaying) + } +} + +function updateCurrentTrack(track) { + $("canvas").remove() + currentTrackID = track + const json = {track: currentTrackID} + fetch('/currentTrack', { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(json) + }) + clearTimeout(timeout) + trackInfo() +} diff --git a/public/js/home.js b/public/js/home.js new file mode 100644 index 00000000..364304b8 --- /dev/null +++ b/public/js/home.js @@ -0,0 +1,129 @@ +const MAXIMUM_ENTRIES_LOADED = 25 +const addRecBtn = document.getElementById("add-rec-btn") +const list = document.getElementById("recommendation-list") +const recText = document.getElementById("caption-text-area") +let username = "badUsernameZQFMGB" + +const addRecommendation = function () { + let rating = -1 + if (document.getElementById("star5").checked) rating = 5 + else if (document.getElementById("star4").checked) rating = 4 + else if (document.getElementById("star3").checked) rating = 3 + else if (document.getElementById("star2").checked) rating = 2 + else if (document.getElementById("star1").checked) rating = 1 + else return false + + if (rating === -1) return false + if (username === "badUsernameZQFMGB") return false + let songID = document.getElementById("secretSongID").innerHTML + if (songID === "-1") return + let songname = document.getElementById("secretSongName").innerHTML + if (songname === "-1") return + let artist = document.getElementById("secretArtist").innerHTML + if (artist === "-1") return + const newRecommendation = { + username: username, + songid: songID, + rating: rating, + caption: recText.value, + songname: songname, + artist: artist + } + + const body = JSON.stringify(newRecommendation) + fetch("/recommendation", { + method: "POST", + body, + headers: { + 'Content-Type': 'application/json' + } + }).then(() => { + document.getElementById("secretSongID").innerHTML = "-1" + document.getElementById("secretArtist").innerHTML = "-1" + document.getElementById("secretSongName").innerHTML = "-1" + getRecommendations() + }) + + document.getElementById("star5").checked = false; + document.getElementById("star4").checked = false; + document.getElementById("star3").checked = false; + document.getElementById("star2").checked = false; + document.getElementById("star1").checked = false; + document.getElementById("songName").value = ""; + recText.setAttribute('placeholder', "Write a note about the song here"); + recText.value = ""; + $("#songsTbody").empty(); +} + +const getUser = function () { + (async () => { + const rawResponse = await fetch('/user', { + method: 'GET' + }) + let usr = await rawResponse.json() + username = usr.user.username + document.getElementById("secretUsername").innerHTML = username + document.getElementById("logoutLink").innerHTML = "Log out " + username + })() +} + +const getRecommendations = function () { + (async () => { + const rawResponse = await fetch('/recommendation', { + method: 'GET' + }) + let res = await rawResponse.json() + + let numEntries = res.length + if (numEntries > MAXIMUM_ENTRIES_LOADED) numEntries = MAXIMUM_ENTRIES_LOADED + + if (numEntries === 0) { + list.innerHTML = + `
\n` + + `
\n` + + `

No recommendations found

\n` + + `
\n` + + `
` + } else { + list.innerHTML = "" + for (let i = numEntries - 1; i >= 0; i--) { + let element = res[i] + let songid = element.songid + let username = element.username + let rating = element.rating + let caption = element.caption + let songname = element.songname + let artist = element.artist + + if (isNaN(rating) || rating < 1 || rating > 5) continue + + let newRecommendation = + `
\n` + + `
\n` + + `
` + username + " rated \"" + songname + "\" by " + artist + `
\n` + + `

` + + for (let j = 0; j < rating; j++) + newRecommendation += `Star` + for (let j = rating; j < 5; j++) + newRecommendation += `Blank Star` + + newRecommendation += `

\n` + + `

` + "\"" + caption + "\"" + `

\n` + + ` \n` + + ` ` + `\n` + + `
` + `\n` + + `
` + + list.innerHTML += newRecommendation + } + } + })() +} + +getUser() +getRecommendations() +addRecBtn.onclick = addRecommendation + diff --git a/public/login.html b/public/login.html new file mode 100644 index 00000000..3ce4660d --- /dev/null +++ b/public/login.html @@ -0,0 +1,47 @@ + + + + Music App + + + + + + +
+ Application Logo +
+

+ MusicApp +

+

+ Please log in +

+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + diff --git a/public/register.html b/public/register.html new file mode 100644 index 00000000..4dc1f16c --- /dev/null +++ b/public/register.html @@ -0,0 +1,41 @@ + + + + Music App + + + + + + +
+ Application Logo +
+

+ Hi +

+

+ You can register here +

+
+
+
+
+ + +
+
+ + +
+ +
+
+ + diff --git a/public/search.js b/public/search.js new file mode 100644 index 00000000..28837666 --- /dev/null +++ b/public/search.js @@ -0,0 +1,68 @@ +let token + +(function getData() { + (async () => { + const rawResponse = await fetch('/token', { + method: 'GET' + }) + let response = await rawResponse.json() + token = response.token + })() +})() + +window.onload = function (e) { + let searchButton = document.getElementById("songSearch") + searchButton.onclick = searchSpotify + let searchInput = document.getElementById("songName") + searchInput.addEventListener("keydown", function (event) { + if (event.key === "Enter") { + event.preventDefault(); + document.getElementById("songSearch").click(); + } + }); + searchInput.onsubmit = searchSpotify +} + +$('#scrollable-table').on('click', 'tbody tr', function (event) { + $(this).addClass('highlight').siblings().removeClass('highlight'); + let songName = $(this).find('td:eq(0)').html(); + let artistName = $(this).find('td:eq(1)').html(); + let idNumber = $(this).find('td:eq(2)').html(); + document.getElementById("secretSongID").innerHTML = idNumber + document.getElementById("secretArtist").innerHTML = artistName + document.getElementById("secretSongName").innerHTML = songName + console.log("currently selected song: " + songName + "\n\t\t" + + "artist: " + artistName + "\n\t\t" + + "id: " + idNumber + "\n\t\t") +}); + +function searchSpotify() { + let songName = document.getElementById("songName").value + + const BASE_URL = 'https://api.spotify.com/v1/search?' + const FETCH_URL = `${BASE_URL}q=track:${songName}&type=track&market=US&limit=10` + + fetch(FETCH_URL, { + method: 'GET', + headers: new Headers({ + Accept: "application/json", + Authorization: "Bearer " + token + }) + }) + .then(response => response.json()) + .then(json => search(json)) + +} + +function search(json) { + let tracks = json.tracks.items + $("tbody").empty() + for (let i = 0; i < tracks.length; i++) { + $("tbody").append("" + + " " + tracks[i].name + "\n" + + " " + tracks[i].artists[0].name + "" + + " " + tracks[i].uri.split('track:')[1] + "" + + " " + ) + } +} diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 00000000..79d74167 --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1,256 @@ +body { + padding: 25px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; + background-color: rgba(0, 0, 0, 0.87) !important; +} + +@media (min-width: 500px) { + body { + overflow: hidden; + } +} + +nav { + margin-bottom: 2%; + opacity: .87; +} + +a { + color: #00B7FF; +} + +h1 { + color: lightgray; +} + +label { + color: white; +} + +.profile { + border-radius: 50%; + width: 20%; +} + +.logo { + width: 5%; + border-radius: 50%; +} + +.text { + color: white; +} + +.side-container { + background-color: #F8F9FA; + color: black; + opacity: .87; + text-align: center; +} + +.post { + text-align: center; + margin: 0; +} + +img { + padding-right: 7px; +} + +.centeredButtons { + text-align: center; +} + +#loginBtn, #signUpBtn { + margin-left: 5px; + margin-right: 5px; +} + +#mainImage { + width: 200px; + height: 200px; +} + +.label { + margin: 2em; +} + +.my-custom-scrollbar { + position: relative; + height: 200px; + overflow: auto; +} + +table { + max-height: 20%; +} + +.table-wrapper-scroll-y { + display: block; +} + +.table tbody tr.highlight td { + background-color: #391A3F; +} + +#current-track { + height: auto; + width: 20%; + -webkit-border-radius: 50%; + -moz-border-radius: 50%; + border-radius: 50%; +} + +.hidden-data { + visibility: hidden; +} + +.leftJustify { + text-align: center; +} + +.results-table { + clear: left; + float: left; + margin-bottom: 1em; +} + +.id-data { + visibility: hidden; +} + +#rec-btn-div { + text-align: center; +} + +.rate { + height: 46px; + padding: 0 10px; + display: inline-block; +} + +.rate:not(:checked) > input { + position: absolute; + top: -9999px; +} + +.rate:not(:checked) > label { + float: right; + width: 1em; + overflow: hidden; + white-space: nowrap; + cursor: pointer; + font-size: 30px; + color: #ccc; +} + +.rate:not(:checked) > label:before { + content: '★ '; +} + +.rate > input:checked ~ label { + color: #6a3075; +} + +.rate:not(:checked) > label:hover, +.rate:not(:checked) > label:hover ~ label { + color: #5a2963; +} + +.rate > input:checked + label:hover, +.rate > input:checked + label:hover ~ label, +.rate > input:checked ~ label:hover, +.rate > input:checked ~ label:hover ~ label, +.rate > label:hover ~ input:checked ~ label { + color: #492151; +} + +.invisible { + display: none; +} + +.player-header { + position: sticky; + top: 0; +} + +.recommendations-container { + height: 55vh; + max-height: 55vh; +} + +.star-rating { + text-align: center; +} + +#play-pause-button { + height: 25px; + width: 25px; + color: rgba(0, 0, 0, 0.87) !important; +} + +#play-pause-button-player { + height: 30px; + width: 30px; + color: rgba(0, 0, 0, 0.87) !important; +} + +.background { + position: absolute; + width: 1000px; + height: 1000px; + top: 0; + left: 0; + z-index: -100; +} + +.content { + position: relative; + z-index: 100; +} + +.nav-right { + float: right; +} + +.card { + opacity: .87; + +} + +.searchButton { + color: #FE3B53 !important; + border-color: #FE3B53 !important; +} + +.recommendationButton { + background-color: #FE3B53 !important; + border-color: #FE3B53 !important; +} + +#loginBtn { + background-color: #7E3BFD; + border-color: #7E3BFD; +} + +#signUpBtn { + background-color: #2D9FFE; + border-color: #2D9FFE; +} + +table::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + border-radius: 10px; + background-color: #F5F5F5; +} + +table::-webkit-scrollbar { + width: 12px; + background-color: #F5F5F5; +} + +table::-webkit-scrollbar-thumb { + border-radius: 10px; + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3); + background-color: #555; +} diff --git a/public/tracks.js b/public/tracks.js new file mode 100644 index 00000000..7d6db6ca --- /dev/null +++ b/public/tracks.js @@ -0,0 +1,4776 @@ +let tracksJson = { + "tracks": { + "href": "https://api.spotify.com/v1/search?query=ipanema&type=track&market=US&offset=0&limit=20", + "items": [ + { + "album": { + "album_type": "single", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/4iMO20EPodreIaEl8qW66y" + }, + "href": "https://api.spotify.com/v1/artists/4iMO20EPodreIaEl8qW66y", + "id": "4iMO20EPodreIaEl8qW66y", + "name": "Still Woozy", + "type": "artist", + "uri": "spotify:artist:4iMO20EPodreIaEl8qW66y" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/1SxVNVRfenIneNQvEdWbzP" + }, + "href": "https://api.spotify.com/v1/albums/1SxVNVRfenIneNQvEdWbzP", + "id": "1SxVNVRfenIneNQvEdWbzP", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/97ee888ffae6b7fa94f47274dcac2759e991ae66", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/8c78e911fe4b710dd87257ee3daefd7397f992f4", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/5a8b7368beb0cd11bd4bf39c253d739585c2e3c0", + "width": 64 + } + ], + "name": "Lately EP", + "release_date": "2019-05-03", + "release_date_precision": "day", + "total_tracks": 5, + "type": "album", + "uri": "spotify:album:1SxVNVRfenIneNQvEdWbzP" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/4iMO20EPodreIaEl8qW66y" + }, + "href": "https://api.spotify.com/v1/artists/4iMO20EPodreIaEl8qW66y", + "id": "4iMO20EPodreIaEl8qW66y", + "name": "Still Woozy", + "type": "artist", + "uri": "spotify:artist:4iMO20EPodreIaEl8qW66y" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/5FxD8fkQZ6KcsSYupDVoSO" + }, + "href": "https://api.spotify.com/v1/artists/5FxD8fkQZ6KcsSYupDVoSO", + "id": "5FxD8fkQZ6KcsSYupDVoSO", + "name": "Omar Apollo", + "type": "artist", + "uri": "spotify:artist:5FxD8fkQZ6KcsSYupDVoSO" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/1CgbNAF3Stnz1Tpipu3xdO" + }, + "href": "https://api.spotify.com/v1/artists/1CgbNAF3Stnz1Tpipu3xdO", + "id": "1CgbNAF3Stnz1Tpipu3xdO", + "name": "Elujay", + "type": "artist", + "uri": "spotify:artist:1CgbNAF3Stnz1Tpipu3xdO" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 127044, + "explicit": false, + "external_ids": { + "isrc": "SE5VF1903328" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/4FJzP6dMX4X7EAazhOTgey" + }, + "href": "https://api.spotify.com/v1/tracks/4FJzP6dMX4X7EAazhOTgey", + "id": "4FJzP6dMX4X7EAazhOTgey", + "is_local": false, + "name": "Ipanema", + "popularity": 62, + "preview_url": "https://p.scdn.co/mp3-preview/ef6799e941aa401c741fb09cd6a4adf5d630e737?cid=774b29d4f13844c495f206cafdad9c86", + "track_number": 2, + "type": "track", + "uri": "spotify:track:4FJzP6dMX4X7EAazhOTgey" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/3pO5VjZ4wOHCMBXOvbMISG" + }, + "href": "https://api.spotify.com/v1/artists/3pO5VjZ4wOHCMBXOvbMISG", + "id": "3pO5VjZ4wOHCMBXOvbMISG", + "name": "Antônio Carlos Jobim", + "type": "artist", + "uri": "spotify:artist:3pO5VjZ4wOHCMBXOvbMISG" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/5fxCwduFydtcmLFrEkePFE" + }, + "href": "https://api.spotify.com/v1/albums/5fxCwduFydtcmLFrEkePFE", + "id": "5fxCwduFydtcmLFrEkePFE", + "images": [ + { + "height": 620, + "url": "https://i.scdn.co/image/17a65f51bcf9aadb2a3d03ef2d0f1c11b68f4057", + "width": 640 + }, + { + "height": 291, + "url": "https://i.scdn.co/image/e27f70e20b8bbfdf0be65cb437cb13887ac1b6fe", + "width": 300 + }, + { + "height": 62, + "url": "https://i.scdn.co/image/4784ee26c5632c00d9a9e3ab66774808e97cc36d", + "width": 64 + } + ], + "name": "Compact Jazz: Antonio Carlos Jobim", + "release_date": "1990-08-21", + "release_date_precision": "day", + "total_tracks": 16, + "type": "album", + "uri": "spotify:album:5fxCwduFydtcmLFrEkePFE" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/3pO5VjZ4wOHCMBXOvbMISG" + }, + "href": "https://api.spotify.com/v1/artists/3pO5VjZ4wOHCMBXOvbMISG", + "id": "3pO5VjZ4wOHCMBXOvbMISG", + "name": "Antônio Carlos Jobim", + "type": "artist", + "uri": "spotify:artist:3pO5VjZ4wOHCMBXOvbMISG" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 322333, + "explicit": false, + "external_ids": { + "isrc": "USPR36307072" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/1AxAPXcUcH3nXXeCeG0kGV" + }, + "href": "https://api.spotify.com/v1/tracks/1AxAPXcUcH3nXXeCeG0kGV", + "id": "1AxAPXcUcH3nXXeCeG0kGV", + "is_local": false, + "name": "The Girl From Ipanema", + "popularity": 66, + "preview_url": null, + "track_number": 1, + "type": "track", + "uri": "spotify:track:1AxAPXcUcH3nXXeCeG0kGV" + }, + { + "album": { + "album_type": "single", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/40JYLbI7UkALG8X4IAOjgC" + }, + "href": "https://api.spotify.com/v1/artists/40JYLbI7UkALG8X4IAOjgC", + "id": "40JYLbI7UkALG8X4IAOjgC", + "name": "Santos", + "type": "artist", + "uri": "spotify:artist:40JYLbI7UkALG8X4IAOjgC" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/7vAQtglvZbAifozL6e3dOK" + }, + "href": "https://api.spotify.com/v1/albums/7vAQtglvZbAifozL6e3dOK", + "id": "7vAQtglvZbAifozL6e3dOK", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/5abccb8874addf82aad803d213c58aab24c3d8a1", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/7fa0891e2462e66ba4307aa402536d0a4eba73e1", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/e071b1c7337ff2fd2b71de1636eab904d0a1b009", + "width": 64 + } + ], + "name": "Ipanema Sunset", + "release_date": "2017-01-03", + "release_date_precision": "day", + "total_tracks": 1, + "type": "album", + "uri": "spotify:album:7vAQtglvZbAifozL6e3dOK" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/40JYLbI7UkALG8X4IAOjgC" + }, + "href": "https://api.spotify.com/v1/artists/40JYLbI7UkALG8X4IAOjgC", + "id": "40JYLbI7UkALG8X4IAOjgC", + "name": "Santos", + "type": "artist", + "uri": "spotify:artist:40JYLbI7UkALG8X4IAOjgC" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 100999, + "explicit": false, + "external_ids": { + "isrc": "TCACW1731755" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/3HY4CldJsd7ydS6bvClzhX" + }, + "href": "https://api.spotify.com/v1/tracks/3HY4CldJsd7ydS6bvClzhX", + "id": "3HY4CldJsd7ydS6bvClzhX", + "is_local": false, + "name": "Ipanema Sunset", + "popularity": 48, + "preview_url": "https://p.scdn.co/mp3-preview/431ef0c534420074244d8448357d8fc4d81ab4dc?cid=774b29d4f13844c495f206cafdad9c86", + "track_number": 1, + "type": "track", + "uri": "spotify:track:3HY4CldJsd7ydS6bvClzhX" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/6Q192DXotxtaysaqNPy5yR" + }, + "href": "https://api.spotify.com/v1/artists/6Q192DXotxtaysaqNPy5yR", + "id": "6Q192DXotxtaysaqNPy5yR", + "name": "Amy Winehouse", + "type": "artist", + "uri": "spotify:artist:6Q192DXotxtaysaqNPy5yR" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/4xdRjOhY9NHmMpI7U3e2c3" + }, + "href": "https://api.spotify.com/v1/albums/4xdRjOhY9NHmMpI7U3e2c3", + "id": "4xdRjOhY9NHmMpI7U3e2c3", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/0b2351da671cccaadfd9661f5814e64eb80d18de", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/ef21e8ccb021a0fbc6b54dde2506a1e80af93acb", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/148bd5ee5991b83d38e6a14a1af8aa78125a4f2e", + "width": 64 + } + ], + "name": "Lioness: Hidden Treasures", + "release_date": "2011-01-01", + "release_date_precision": "day", + "total_tracks": 12, + "type": "album", + "uri": "spotify:album:4xdRjOhY9NHmMpI7U3e2c3" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/6Q192DXotxtaysaqNPy5yR" + }, + "href": "https://api.spotify.com/v1/artists/6Q192DXotxtaysaqNPy5yR", + "id": "6Q192DXotxtaysaqNPy5yR", + "name": "Amy Winehouse", + "type": "artist", + "uri": "spotify:artist:6Q192DXotxtaysaqNPy5yR" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 166960, + "explicit": false, + "external_ids": { + "isrc": "GBUM71110103" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/7F5rIyOI88zk36AiBXC8xq" + }, + "href": "https://api.spotify.com/v1/tracks/7F5rIyOI88zk36AiBXC8xq", + "id": "7F5rIyOI88zk36AiBXC8xq", + "is_local": false, + "name": "The Girl From Ipanema", + "popularity": 59, + "preview_url": null, + "track_number": 7, + "type": "track", + "uri": "spotify:track:7F5rIyOI88zk36AiBXC8xq" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/1Mxqyy3pSjf8kZZL4QVxS0" + }, + "href": "https://api.spotify.com/v1/artists/1Mxqyy3pSjf8kZZL4QVxS0", + "id": "1Mxqyy3pSjf8kZZL4QVxS0", + "name": "Frank Sinatra", + "type": "artist", + "uri": "spotify:artist:1Mxqyy3pSjf8kZZL4QVxS0" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/3pO5VjZ4wOHCMBXOvbMISG" + }, + "href": "https://api.spotify.com/v1/artists/3pO5VjZ4wOHCMBXOvbMISG", + "id": "3pO5VjZ4wOHCMBXOvbMISG", + "name": "Antônio Carlos Jobim", + "type": "artist", + "uri": "spotify:artist:3pO5VjZ4wOHCMBXOvbMISG" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/5280NkLzxe2w7K1JW9I8jX" + }, + "href": "https://api.spotify.com/v1/albums/5280NkLzxe2w7K1JW9I8jX", + "id": "5280NkLzxe2w7K1JW9I8jX", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/dcec22595b19a9df4ca8a995a2f7935a12068a96", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/7d3118c6c71e4257d61a03807d33b68ffad8b031", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/84e939c54fd8b088ddff82e80e237f4d6821f3d2", + "width": 64 + } + ], + "name": "Sinatra/Jobim: The Complete Reprise Recordings", + "release_date": "1967-03", + "release_date_precision": "month", + "total_tracks": 20, + "type": "album", + "uri": "spotify:album:5280NkLzxe2w7K1JW9I8jX" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/3pO5VjZ4wOHCMBXOvbMISG" + }, + "href": "https://api.spotify.com/v1/artists/3pO5VjZ4wOHCMBXOvbMISG", + "id": "3pO5VjZ4wOHCMBXOvbMISG", + "name": "Antônio Carlos Jobim", + "type": "artist", + "uri": "spotify:artist:3pO5VjZ4wOHCMBXOvbMISG" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/1Mxqyy3pSjf8kZZL4QVxS0" + }, + "href": "https://api.spotify.com/v1/artists/1Mxqyy3pSjf8kZZL4QVxS0", + "id": "1Mxqyy3pSjf8kZZL4QVxS0", + "name": "Frank Sinatra", + "type": "artist", + "uri": "spotify:artist:1Mxqyy3pSjf8kZZL4QVxS0" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 200360, + "explicit": false, + "external_ids": { + "isrc": "USRH10800977" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/5N6pQ2vYtd3Rb9w7LC8PZ9" + }, + "href": "https://api.spotify.com/v1/tracks/5N6pQ2vYtd3Rb9w7LC8PZ9", + "id": "5N6pQ2vYtd3Rb9w7LC8PZ9", + "is_local": false, + "name": "The Girl From Ipanema", + "popularity": 57, + "preview_url": null, + "track_number": 1, + "type": "track", + "uri": "spotify:track:5N6pQ2vYtd3Rb9w7LC8PZ9" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/7v4imS0moSyGdXyLgVTIV7" + }, + "href": "https://api.spotify.com/v1/artists/7v4imS0moSyGdXyLgVTIV7", + "id": "7v4imS0moSyGdXyLgVTIV7", + "name": "Nat King Cole", + "type": "artist", + "uri": "spotify:artist:7v4imS0moSyGdXyLgVTIV7" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/2IFGaozoXFA3m9IUH2Hcc4" + }, + "href": "https://api.spotify.com/v1/albums/2IFGaozoXFA3m9IUH2Hcc4", + "id": "2IFGaozoXFA3m9IUH2Hcc4", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/1fabde4fcac6be70c42e9473ac5a4f66f1eca0d3", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/dc263efc82b425d4d62f9ec752ecd0062fa9d8be", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/4170fcb827df1fd7ebe9489239e42fef76b544ab", + "width": 64 + } + ], + "name": "Ultimate Nat King Cole", + "release_date": "2019-03-15", + "release_date_precision": "day", + "total_tracks": 21, + "type": "album", + "uri": "spotify:album:2IFGaozoXFA3m9IUH2Hcc4" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/7v4imS0moSyGdXyLgVTIV7" + }, + "href": "https://api.spotify.com/v1/artists/7v4imS0moSyGdXyLgVTIV7", + "id": "7v4imS0moSyGdXyLgVTIV7", + "name": "Nat King Cole", + "type": "artist", + "uri": "spotify:artist:7v4imS0moSyGdXyLgVTIV7" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/06nevPmNVfWUXyZkccahL8" + }, + "href": "https://api.spotify.com/v1/artists/06nevPmNVfWUXyZkccahL8", + "id": "06nevPmNVfWUXyZkccahL8", + "name": "Gregory Porter", + "type": "artist", + "uri": "spotify:artist:06nevPmNVfWUXyZkccahL8" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 174773, + "explicit": false, + "external_ids": { + "isrc": "USUG11900004" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/3niaqXwt6bSu5HCXJpaee7" + }, + "href": "https://api.spotify.com/v1/tracks/3niaqXwt6bSu5HCXJpaee7", + "id": "3niaqXwt6bSu5HCXJpaee7", + "is_local": false, + "name": "The Girl From Ipanema", + "popularity": 54, + "preview_url": null, + "track_number": 21, + "type": "track", + "uri": "spotify:track:3niaqXwt6bSu5HCXJpaee7" + }, + { + "album": { + "album_type": "compilation", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/1Mxqyy3pSjf8kZZL4QVxS0" + }, + "href": "https://api.spotify.com/v1/artists/1Mxqyy3pSjf8kZZL4QVxS0", + "id": "1Mxqyy3pSjf8kZZL4QVxS0", + "name": "Frank Sinatra", + "type": "artist", + "uri": "spotify:artist:1Mxqyy3pSjf8kZZL4QVxS0" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/3i67sGIVw8EBlgfSRv3Lj2" + }, + "href": "https://api.spotify.com/v1/albums/3i67sGIVw8EBlgfSRv3Lj2", + "id": "3i67sGIVw8EBlgfSRv3Lj2", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/d32aeff49d628f9c554fa561fbecdd02cf867623", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/622fb28df75069765a48ddef9962aeea05c17c8b", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/9c4a8030d73a43e30bfeaac05d9e6f399cd13cd1", + "width": 64 + } + ], + "name": "Nothing But The Best (Remastered)", + "release_date": "2008-01-01", + "release_date_precision": "day", + "total_tracks": 22, + "type": "album", + "uri": "spotify:album:3i67sGIVw8EBlgfSRv3Lj2" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/1Mxqyy3pSjf8kZZL4QVxS0" + }, + "href": "https://api.spotify.com/v1/artists/1Mxqyy3pSjf8kZZL4QVxS0", + "id": "1Mxqyy3pSjf8kZZL4QVxS0", + "name": "Frank Sinatra", + "type": "artist", + "uri": "spotify:artist:1Mxqyy3pSjf8kZZL4QVxS0" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/3pO5VjZ4wOHCMBXOvbMISG" + }, + "href": "https://api.spotify.com/v1/artists/3pO5VjZ4wOHCMBXOvbMISG", + "id": "3pO5VjZ4wOHCMBXOvbMISG", + "name": "Antônio Carlos Jobim", + "type": "artist", + "uri": "spotify:artist:3pO5VjZ4wOHCMBXOvbMISG" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 194040, + "explicit": false, + "external_ids": { + "isrc": "USRH10723028" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/3NdHYXQK5gkFFEZvSMVVj6" + }, + "href": "https://api.spotify.com/v1/tracks/3NdHYXQK5gkFFEZvSMVVj6", + "id": "3NdHYXQK5gkFFEZvSMVVj6", + "is_local": false, + "name": "The Girl From Ipanema - 2008 Remastered", + "popularity": 51, + "preview_url": null, + "track_number": 7, + "type": "track", + "uri": "spotify:track:3NdHYXQK5gkFFEZvSMVVj6" + }, + { + "album": { + "album_type": "compilation", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of" + }, + "href": "https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of", + "id": "0LyfQWJT6nXafLPZqxe9Of", + "name": "Various Artists", + "type": "artist", + "uri": "spotify:artist:0LyfQWJT6nXafLPZqxe9Of" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/2KJsAjK0T585kFmFA3O7vr" + }, + "href": "https://api.spotify.com/v1/albums/2KJsAjK0T585kFmFA3O7vr", + "id": "2KJsAjK0T585kFmFA3O7vr", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/46af3b6f48f0908961625d4270199bfe4bb774cf", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/c8763a82487948994f59e50f127ad5802469719b", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/ef429402982562043dd99dc9e642de81926b5dc9", + "width": 64 + } + ], + "name": "Bossa Nova", + "release_date": "2004-01-01", + "release_date_precision": "day", + "total_tracks": 14, + "type": "album", + "uri": "spotify:album:2KJsAjK0T585kFmFA3O7vr" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/5b2ylVrhm7GKFoUjuNk1Op" + }, + "href": "https://api.spotify.com/v1/artists/5b2ylVrhm7GKFoUjuNk1Op", + "id": "5b2ylVrhm7GKFoUjuNk1Op", + "name": "Vinícius", + "type": "artist", + "uri": "spotify:artist:5b2ylVrhm7GKFoUjuNk1Op" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/3pO5VjZ4wOHCMBXOvbMISG" + }, + "href": "https://api.spotify.com/v1/artists/3pO5VjZ4wOHCMBXOvbMISG", + "id": "3pO5VjZ4wOHCMBXOvbMISG", + "name": "Antônio Carlos Jobim", + "type": "artist", + "uri": "spotify:artist:3pO5VjZ4wOHCMBXOvbMISG" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 150613, + "explicit": false, + "external_ids": { + "isrc": "ARG990900583" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/13phYi072bj5ibxBx4VhQC" + }, + "href": "https://api.spotify.com/v1/tracks/13phYi072bj5ibxBx4VhQC", + "id": "13phYi072bj5ibxBx4VhQC", + "is_local": false, + "name": "Garota De Ipanema", + "popularity": 58, + "preview_url": "https://p.scdn.co/mp3-preview/6e1d00a2d7b3bd178fc4d02001b3ff634e825f1a?cid=774b29d4f13844c495f206cafdad9c86", + "track_number": 3, + "type": "track", + "uri": "spotify:track:13phYi072bj5ibxBx4VhQC" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/2JfVCMa3FlvQRlLT5uH9zb" + }, + "href": "https://api.spotify.com/v1/artists/2JfVCMa3FlvQRlLT5uH9zb", + "id": "2JfVCMa3FlvQRlLT5uH9zb", + "name": "Nancy Wilson", + "type": "artist", + "uri": "spotify:artist:2JfVCMa3FlvQRlLT5uH9zb" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/4qgiPr8Wg6HpqtfZBzcKqt" + }, + "href": "https://api.spotify.com/v1/albums/4qgiPr8Wg6HpqtfZBzcKqt", + "id": "4qgiPr8Wg6HpqtfZBzcKqt", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/ab67616d0000b273f32c2640357c4e7067a0e42e", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/ab67616d00001e02f32c2640357c4e7067a0e42e", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/ab67616d00004851f32c2640357c4e7067a0e42e", + "width": 64 + } + ], + "name": "How Glad I Am", + "release_date": "1964-01-01", + "release_date_precision": "day", + "total_tracks": 10, + "type": "album", + "uri": "spotify:album:4qgiPr8Wg6HpqtfZBzcKqt" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/2JfVCMa3FlvQRlLT5uH9zb" + }, + "href": "https://api.spotify.com/v1/artists/2JfVCMa3FlvQRlLT5uH9zb", + "id": "2JfVCMa3FlvQRlLT5uH9zb", + "name": "Nancy Wilson", + "type": "artist", + "uri": "spotify:artist:2JfVCMa3FlvQRlLT5uH9zb" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 134213, + "explicit": false, + "external_ids": { + "isrc": "USCA29601301" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/2chTka18XL8un05Pkb8Lis" + }, + "href": "https://api.spotify.com/v1/tracks/2chTka18XL8un05Pkb8Lis", + "id": "2chTka18XL8un05Pkb8Lis", + "is_local": false, + "name": "The Boy From Ipanema", + "popularity": 52, + "preview_url": null, + "track_number": 3, + "type": "track", + "uri": "spotify:track:2chTka18XL8un05Pkb8Lis" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0FMucZsEnCxs5pqBjHjIc8" + }, + "href": "https://api.spotify.com/v1/artists/0FMucZsEnCxs5pqBjHjIc8", + "id": "0FMucZsEnCxs5pqBjHjIc8", + "name": "Stan Getz", + "type": "artist", + "uri": "spotify:artist:0FMucZsEnCxs5pqBjHjIc8" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/3VvwZy2KLUgi3QYmx7oiCA" + }, + "href": "https://api.spotify.com/v1/albums/3VvwZy2KLUgi3QYmx7oiCA", + "id": "3VvwZy2KLUgi3QYmx7oiCA", + "images": [ + { + "height": 600, + "url": "https://i.scdn.co/image/5edf9962c5bf32cc2f3c1afca4cb02540949cec9", + "width": 600 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/689e8de1add2cf94459d7da3e1217755e520b988", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/81e56f25454f9b5e8cebc452efb8e4f694202d6b", + "width": 64 + } + ], + "name": "The Girl from Ipanema", + "release_date": "2016-04-29", + "release_date_precision": "day", + "total_tracks": 50, + "type": "album", + "uri": "spotify:album:3VvwZy2KLUgi3QYmx7oiCA" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/77ZUbcdoU5KCPHNUl8bgQy" + }, + "href": "https://api.spotify.com/v1/artists/77ZUbcdoU5KCPHNUl8bgQy", + "id": "77ZUbcdoU5KCPHNUl8bgQy", + "name": "João Gilberto", + "type": "artist", + "uri": "spotify:artist:77ZUbcdoU5KCPHNUl8bgQy" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0FMucZsEnCxs5pqBjHjIc8" + }, + "href": "https://api.spotify.com/v1/artists/0FMucZsEnCxs5pqBjHjIc8", + "id": "0FMucZsEnCxs5pqBjHjIc8", + "name": "Stan Getz", + "type": "artist", + "uri": "spotify:artist:0FMucZsEnCxs5pqBjHjIc8" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/5rX2c1zow6hCph8PnnU3kF" + }, + "href": "https://api.spotify.com/v1/artists/5rX2c1zow6hCph8PnnU3kF", + "id": "5rX2c1zow6hCph8PnnU3kF", + "name": "Astrud Gilberto", + "type": "artist", + "uri": "spotify:artist:5rX2c1zow6hCph8PnnU3kF" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 169026, + "explicit": false, + "external_ids": { + "isrc": "USPR36312115" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/43p5Olz6Cis0ZCYcqgQ18y" + }, + "href": "https://api.spotify.com/v1/tracks/43p5Olz6Cis0ZCYcqgQ18y", + "id": "43p5Olz6Cis0ZCYcqgQ18y", + "is_local": false, + "name": "The Girl from Ipanema", + "popularity": 49, + "preview_url": null, + "track_number": 1, + "type": "track", + "uri": "spotify:track:43p5Olz6Cis0ZCYcqgQ18y" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0FMucZsEnCxs5pqBjHjIc8" + }, + "href": "https://api.spotify.com/v1/artists/0FMucZsEnCxs5pqBjHjIc8", + "id": "0FMucZsEnCxs5pqBjHjIc8", + "name": "Stan Getz", + "type": "artist", + "uri": "spotify:artist:0FMucZsEnCxs5pqBjHjIc8" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/77ZUbcdoU5KCPHNUl8bgQy" + }, + "href": "https://api.spotify.com/v1/artists/77ZUbcdoU5KCPHNUl8bgQy", + "id": "77ZUbcdoU5KCPHNUl8bgQy", + "name": "João Gilberto", + "type": "artist", + "uri": "spotify:artist:77ZUbcdoU5KCPHNUl8bgQy" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/2W6Hvrtg2Zpc9dW4aBDbdP" + }, + "href": "https://api.spotify.com/v1/albums/2W6Hvrtg2Zpc9dW4aBDbdP", + "id": "2W6Hvrtg2Zpc9dW4aBDbdP", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/d479100f74473f863ed486498ac373836dab895d", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/0b116b02ee79d735a434fa9e80744e3691280d34", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/8a9c5661a9967fdf7451c36a0cf33118da72ce36", + "width": 64 + } + ], + "name": "Getz/Gilberto (Expanded Edition)", + "release_date": "2014-05-27", + "release_date_precision": "day", + "total_tracks": 18, + "type": "album", + "uri": "spotify:album:2W6Hvrtg2Zpc9dW4aBDbdP" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0FMucZsEnCxs5pqBjHjIc8" + }, + "href": "https://api.spotify.com/v1/artists/0FMucZsEnCxs5pqBjHjIc8", + "id": "0FMucZsEnCxs5pqBjHjIc8", + "name": "Stan Getz", + "type": "artist", + "uri": "spotify:artist:0FMucZsEnCxs5pqBjHjIc8" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/77ZUbcdoU5KCPHNUl8bgQy" + }, + "href": "https://api.spotify.com/v1/artists/77ZUbcdoU5KCPHNUl8bgQy", + "id": "77ZUbcdoU5KCPHNUl8bgQy", + "name": "João Gilberto", + "type": "artist", + "uri": "spotify:artist:77ZUbcdoU5KCPHNUl8bgQy" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/5rX2c1zow6hCph8PnnU3kF" + }, + "href": "https://api.spotify.com/v1/artists/5rX2c1zow6hCph8PnnU3kF", + "id": "5rX2c1zow6hCph8PnnU3kF", + "name": "Astrud Gilberto", + "type": "artist", + "uri": "spotify:artist:5rX2c1zow6hCph8PnnU3kF" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/3pO5VjZ4wOHCMBXOvbMISG" + }, + "href": "https://api.spotify.com/v1/artists/3pO5VjZ4wOHCMBXOvbMISG", + "id": "3pO5VjZ4wOHCMBXOvbMISG", + "name": "Antônio Carlos Jobim", + "type": "artist", + "uri": "spotify:artist:3pO5VjZ4wOHCMBXOvbMISG" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 167533, + "explicit": false, + "external_ids": { + "isrc": "USUMG0000425" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/4qGfJb2KByjvzrwo8HNibg" + }, + "href": "https://api.spotify.com/v1/tracks/4qGfJb2KByjvzrwo8HNibg", + "id": "4qGfJb2KByjvzrwo8HNibg", + "is_local": false, + "name": "The Girl From Ipanema - Single Version", + "popularity": 53, + "preview_url": null, + "track_number": 17, + "type": "track", + "uri": "spotify:track:4qGfJb2KByjvzrwo8HNibg" + }, + { + "album": { + "album_type": "single", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/7Jd8IfE12z0jbepgMaVpXl" + }, + "href": "https://api.spotify.com/v1/artists/7Jd8IfE12z0jbepgMaVpXl", + "id": "7Jd8IfE12z0jbepgMaVpXl", + "name": "Davide Ambrosia", + "type": "artist", + "uri": "spotify:artist:7Jd8IfE12z0jbepgMaVpXl" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/38OsqZOKQwGVhrFlmNqfUw" + }, + "href": "https://api.spotify.com/v1/albums/38OsqZOKQwGVhrFlmNqfUw", + "id": "38OsqZOKQwGVhrFlmNqfUw", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/b81c47002f5b704d02c71d96e2f0f85c03a5f1f5", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/46eac161587fef906fb66b647351893f78b0a4d8", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/51419aac08379318de893b66c7321a38f9a6771a", + "width": 64 + } + ], + "name": "Grand Hotel", + "release_date": "2015", + "release_date_precision": "year", + "total_tracks": 3, + "type": "album", + "uri": "spotify:album:38OsqZOKQwGVhrFlmNqfUw" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/7Jd8IfE12z0jbepgMaVpXl" + }, + "href": "https://api.spotify.com/v1/artists/7Jd8IfE12z0jbepgMaVpXl", + "id": "7Jd8IfE12z0jbepgMaVpXl", + "name": "Davide Ambrosia", + "type": "artist", + "uri": "spotify:artist:7Jd8IfE12z0jbepgMaVpXl" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 203318, + "explicit": false, + "external_ids": { + "isrc": "SE5IB1700871" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/5gzgkcNPHlSgnoqdEOYLfC" + }, + "href": "https://api.spotify.com/v1/tracks/5gzgkcNPHlSgnoqdEOYLfC", + "id": "5gzgkcNPHlSgnoqdEOYLfC", + "is_local": false, + "name": "Girl From Ipanema", + "popularity": 54, + "preview_url": "https://p.scdn.co/mp3-preview/c5e542f520175b1576f99b7fea889c90ec24ee15?cid=774b29d4f13844c495f206cafdad9c86", + "track_number": 2, + "type": "track", + "uri": "spotify:track:5gzgkcNPHlSgnoqdEOYLfC" + }, + { + "album": { + "album_type": "single", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0g5AouvHCjswMZiQheyRNK" + }, + "href": "https://api.spotify.com/v1/artists/0g5AouvHCjswMZiQheyRNK", + "id": "0g5AouvHCjswMZiQheyRNK", + "name": "Rio Mendes", + "type": "artist", + "uri": "spotify:artist:0g5AouvHCjswMZiQheyRNK" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/6ROavccgDMsZS13Lx7NWYI" + }, + "href": "https://api.spotify.com/v1/albums/6ROavccgDMsZS13Lx7NWYI", + "id": "6ROavccgDMsZS13Lx7NWYI", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/cb68333e434ea54c76b4c4e5c1f6915bdbd3d402", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/709d5632b565207922cbf97782940471773b4869", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/0da1843bcaa29395e708a8cc46956df9adefbe4d", + "width": 64 + } + ], + "name": "Bossa Nova Sounds EP", + "release_date": "2019-07-12", + "release_date_precision": "day", + "total_tracks": 3, + "type": "album", + "uri": "spotify:album:6ROavccgDMsZS13Lx7NWYI" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0g5AouvHCjswMZiQheyRNK" + }, + "href": "https://api.spotify.com/v1/artists/0g5AouvHCjswMZiQheyRNK", + "id": "0g5AouvHCjswMZiQheyRNK", + "name": "Rio Mendes", + "type": "artist", + "uri": "spotify:artist:0g5AouvHCjswMZiQheyRNK" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 180860, + "explicit": false, + "external_ids": { + "isrc": "QZAKB1910099" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/4LFygoMviIwMfAlxBz821e" + }, + "href": "https://api.spotify.com/v1/tracks/4LFygoMviIwMfAlxBz821e", + "id": "4LFygoMviIwMfAlxBz821e", + "is_local": false, + "name": "The Girl From Ipanema", + "popularity": 49, + "preview_url": "https://p.scdn.co/mp3-preview/cd22faff356c42cce21008b8f1d0ebfeee881b78?cid=774b29d4f13844c495f206cafdad9c86", + "track_number": 3, + "type": "track", + "uri": "spotify:track:4LFygoMviIwMfAlxBz821e" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0ldU0QJm31y0d6f57R1G2A" + }, + "href": "https://api.spotify.com/v1/artists/0ldU0QJm31y0d6f57R1G2A", + "id": "0ldU0QJm31y0d6f57R1G2A", + "name": "Oscar Peterson Trio", + "type": "artist", + "uri": "spotify:artist:0ldU0QJm31y0d6f57R1G2A" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/7BVfV9OGD9tYdy7Jr5JLbt" + }, + "href": "https://api.spotify.com/v1/albums/7BVfV9OGD9tYdy7Jr5JLbt", + "id": "7BVfV9OGD9tYdy7Jr5JLbt", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/5d2bfdfd801cb2fbee0fc485cc734c9dbff01127", + "width": 612 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/87918e967a9f8bdbb3fb13d898027cc63c75df5c", + "width": 287 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/14f304b86d618313a428ee1ff003e0b8dd9506d4", + "width": 61 + } + ], + "name": "We Get Requests", + "release_date": "1964", + "release_date_precision": "year", + "total_tracks": 10, + "type": "album", + "uri": "spotify:album:7BVfV9OGD9tYdy7Jr5JLbt" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0ldU0QJm31y0d6f57R1G2A" + }, + "href": "https://api.spotify.com/v1/artists/0ldU0QJm31y0d6f57R1G2A", + "id": "0ldU0QJm31y0d6f57R1G2A", + "name": "Oscar Peterson Trio", + "type": "artist", + "uri": "spotify:artist:0ldU0QJm31y0d6f57R1G2A" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 235466, + "explicit": false, + "external_ids": { + "isrc": "USF096425330" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/04n2zZCoMIw7r0NNTN1hMg" + }, + "href": "https://api.spotify.com/v1/tracks/04n2zZCoMIw7r0NNTN1hMg", + "id": "04n2zZCoMIw7r0NNTN1hMg", + "is_local": false, + "name": "Girl From Ipanema", + "popularity": 44, + "preview_url": null, + "track_number": 7, + "type": "track", + "uri": "spotify:track:04n2zZCoMIw7r0NNTN1hMg" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0FMucZsEnCxs5pqBjHjIc8" + }, + "href": "https://api.spotify.com/v1/artists/0FMucZsEnCxs5pqBjHjIc8", + "id": "0FMucZsEnCxs5pqBjHjIc8", + "name": "Stan Getz", + "type": "artist", + "uri": "spotify:artist:0FMucZsEnCxs5pqBjHjIc8" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/77ZUbcdoU5KCPHNUl8bgQy" + }, + "href": "https://api.spotify.com/v1/artists/77ZUbcdoU5KCPHNUl8bgQy", + "id": "77ZUbcdoU5KCPHNUl8bgQy", + "name": "João Gilberto", + "type": "artist", + "uri": "spotify:artist:77ZUbcdoU5KCPHNUl8bgQy" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/2W6Hvrtg2Zpc9dW4aBDbdP" + }, + "href": "https://api.spotify.com/v1/albums/2W6Hvrtg2Zpc9dW4aBDbdP", + "id": "2W6Hvrtg2Zpc9dW4aBDbdP", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/d479100f74473f863ed486498ac373836dab895d", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/0b116b02ee79d735a434fa9e80744e3691280d34", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/8a9c5661a9967fdf7451c36a0cf33118da72ce36", + "width": 64 + } + ], + "name": "Getz/Gilberto (Expanded Edition)", + "release_date": "2014-05-27", + "release_date_precision": "day", + "total_tracks": 18, + "type": "album", + "uri": "spotify:album:2W6Hvrtg2Zpc9dW4aBDbdP" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0FMucZsEnCxs5pqBjHjIc8" + }, + "href": "https://api.spotify.com/v1/artists/0FMucZsEnCxs5pqBjHjIc8", + "id": "0FMucZsEnCxs5pqBjHjIc8", + "name": "Stan Getz", + "type": "artist", + "uri": "spotify:artist:0FMucZsEnCxs5pqBjHjIc8" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/77ZUbcdoU5KCPHNUl8bgQy" + }, + "href": "https://api.spotify.com/v1/artists/77ZUbcdoU5KCPHNUl8bgQy", + "id": "77ZUbcdoU5KCPHNUl8bgQy", + "name": "João Gilberto", + "type": "artist", + "uri": "spotify:artist:77ZUbcdoU5KCPHNUl8bgQy" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/5rX2c1zow6hCph8PnnU3kF" + }, + "href": "https://api.spotify.com/v1/artists/5rX2c1zow6hCph8PnnU3kF", + "id": "5rX2c1zow6hCph8PnnU3kF", + "name": "Astrud Gilberto", + "type": "artist", + "uri": "spotify:artist:5rX2c1zow6hCph8PnnU3kF" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 320253, + "explicit": false, + "external_ids": { + "isrc": "USPR36305146" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/7znjbX9XdoQayIrVNdd50Z" + }, + "href": "https://api.spotify.com/v1/tracks/7znjbX9XdoQayIrVNdd50Z", + "id": "7znjbX9XdoQayIrVNdd50Z", + "is_local": false, + "name": "The Girl From Ipanema", + "popularity": 45, + "preview_url": null, + "track_number": 1, + "type": "track", + "uri": "spotify:track:7znjbX9XdoQayIrVNdd50Z" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/6snr2uV0JQDKLW9yWzRkPj" + }, + "href": "https://api.spotify.com/v1/artists/6snr2uV0JQDKLW9yWzRkPj", + "id": "6snr2uV0JQDKLW9yWzRkPj", + "name": "Funkmammoth", + "type": "artist", + "uri": "spotify:artist:6snr2uV0JQDKLW9yWzRkPj" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/4ldnVVxGWI1MHKYLMSX4Om" + }, + "href": "https://api.spotify.com/v1/albums/4ldnVVxGWI1MHKYLMSX4Om", + "id": "4ldnVVxGWI1MHKYLMSX4Om", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/a4fc69346e6081994aeb74eff45b9c04919f3b5f", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/4e9ae6d7ed8c1c9186881659a4894c3700cdcfc4", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/34db83cf641b554986fd5046f3388b005dc0cbb6", + "width": 64 + } + ], + "name": "Es Tas South", + "release_date": "2017-07-24", + "release_date_precision": "day", + "total_tracks": 14, + "type": "album", + "uri": "spotify:album:4ldnVVxGWI1MHKYLMSX4Om" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/6snr2uV0JQDKLW9yWzRkPj" + }, + "href": "https://api.spotify.com/v1/artists/6snr2uV0JQDKLW9yWzRkPj", + "id": "6snr2uV0JQDKLW9yWzRkPj", + "name": "Funkmammoth", + "type": "artist", + "uri": "spotify:artist:6snr2uV0JQDKLW9yWzRkPj" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 127228, + "explicit": false, + "external_ids": { + "isrc": "TCADE1768521" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/6l8ZAqy48pBSGJtUikKNYr" + }, + "href": "https://api.spotify.com/v1/tracks/6l8ZAqy48pBSGJtUikKNYr", + "id": "6l8ZAqy48pBSGJtUikKNYr", + "is_local": false, + "name": "Ipanema", + "popularity": 45, + "preview_url": "https://p.scdn.co/mp3-preview/b21542f3668a0e0714879248cf23aa2a5c326597?cid=774b29d4f13844c495f206cafdad9c86", + "track_number": 3, + "type": "track", + "uri": "spotify:track:6l8ZAqy48pBSGJtUikKNYr" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/5B6H1Dq77AV1LZWrbNsuH5" + }, + "href": "https://api.spotify.com/v1/artists/5B6H1Dq77AV1LZWrbNsuH5", + "id": "5B6H1Dq77AV1LZWrbNsuH5", + "name": "Jarabe De Palo", + "type": "artist", + "uri": "spotify:artist:5B6H1Dq77AV1LZWrbNsuH5" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/46HlKlgX5YULMqWAs5yazT" + }, + "href": "https://api.spotify.com/v1/albums/46HlKlgX5YULMqWAs5yazT", + "id": "46HlKlgX5YULMqWAs5yazT", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/b78bbc8e03eb23bfb2c25a6f7fdc3fd2758ecb84", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/6211434d2e6a7faec0c0cacb879ee45ca1a1cb06", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/c47e8cad88d676e8eba200df5fb2462c989026bd", + "width": 64 + } + ], + "name": "En la vida conocí mujer igual a la Flaca: 20 años", + "release_date": "2017-12-01", + "release_date_precision": "day", + "total_tracks": 59, + "type": "album", + "uri": "spotify:album:46HlKlgX5YULMqWAs5yazT" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/5B6H1Dq77AV1LZWrbNsuH5" + }, + "href": "https://api.spotify.com/v1/artists/5B6H1Dq77AV1LZWrbNsuH5", + "id": "5B6H1Dq77AV1LZWrbNsuH5", + "name": "Jarabe De Palo", + "type": "artist", + "uri": "spotify:artist:5B6H1Dq77AV1LZWrbNsuH5" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 251480, + "explicit": false, + "external_ids": { + "isrc": "ES5010500041" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/1QgfZsvA8tpsMVs1EylzYU" + }, + "href": "https://api.spotify.com/v1/tracks/1QgfZsvA8tpsMVs1EylzYU", + "id": "1QgfZsvA8tpsMVs1EylzYU", + "is_local": false, + "name": "La chica de Ipanema", + "popularity": 50, + "preview_url": "https://p.scdn.co/mp3-preview/66dcf8b88a92022ed0944ea01bd01be028e36f66?cid=774b29d4f13844c495f206cafdad9c86", + "track_number": 44, + "type": "track", + "uri": "spotify:track:1QgfZsvA8tpsMVs1EylzYU" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/5z1VAFwT35EVvCp1XlZZuL" + }, + "href": "https://api.spotify.com/v1/artists/5z1VAFwT35EVvCp1XlZZuL", + "id": "5z1VAFwT35EVvCp1XlZZuL", + "name": "Diana Krall", + "type": "artist", + "uri": "spotify:artist:5z1VAFwT35EVvCp1XlZZuL" + } + ], + "available_markets": [ + "CA", + "MX", + "US" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/30i0HL1NsQ2xcMhgmQ7rvi" + }, + "href": "https://api.spotify.com/v1/albums/30i0HL1NsQ2xcMhgmQ7rvi", + "id": "30i0HL1NsQ2xcMhgmQ7rvi", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/b1ea3d77eb57ff550f885a4395baa8263dc96f47", + "width": 634 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/513b79b9112f13ab241d198b178d1e3fc4dc9e36", + "width": 297 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/90fbf8c29e1e80386f9ae6543f8124441d0c5056", + "width": 63 + } + ], + "name": "Quiet Nights", + "release_date": "2009-01-01", + "release_date_precision": "day", + "total_tracks": 12, + "type": "album", + "uri": "spotify:album:30i0HL1NsQ2xcMhgmQ7rvi" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/5z1VAFwT35EVvCp1XlZZuL" + }, + "href": "https://api.spotify.com/v1/artists/5z1VAFwT35EVvCp1XlZZuL", + "id": "5z1VAFwT35EVvCp1XlZZuL", + "name": "Diana Krall", + "type": "artist", + "uri": "spotify:artist:5z1VAFwT35EVvCp1XlZZuL" + } + ], + "available_markets": [ + "CA", + "MX", + "US" + ], + "disc_number": 1, + "duration_ms": 292880, + "explicit": false, + "external_ids": { + "isrc": "USUM70951271" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/1iqDYEuA23P28l0lRsNSFs" + }, + "href": "https://api.spotify.com/v1/tracks/1iqDYEuA23P28l0lRsNSFs", + "id": "1iqDYEuA23P28l0lRsNSFs", + "is_local": false, + "name": "The Boy From Ipanema", + "popularity": 37, + "preview_url": null, + "track_number": 4, + "type": "track", + "uri": "spotify:track:1iqDYEuA23P28l0lRsNSFs" + }, + { + "album": { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/1Mxqyy3pSjf8kZZL4QVxS0" + }, + "href": "https://api.spotify.com/v1/artists/1Mxqyy3pSjf8kZZL4QVxS0", + "id": "1Mxqyy3pSjf8kZZL4QVxS0", + "name": "Frank Sinatra", + "type": "artist", + "uri": "spotify:artist:1Mxqyy3pSjf8kZZL4QVxS0" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/3pO5VjZ4wOHCMBXOvbMISG" + }, + "href": "https://api.spotify.com/v1/artists/3pO5VjZ4wOHCMBXOvbMISG", + "id": "3pO5VjZ4wOHCMBXOvbMISG", + "name": "Antônio Carlos Jobim", + "type": "artist", + "uri": "spotify:artist:3pO5VjZ4wOHCMBXOvbMISG" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/0LYpJGx3fHoTmKOtKuMWtQ" + }, + "href": "https://api.spotify.com/v1/albums/0LYpJGx3fHoTmKOtKuMWtQ", + "id": "0LYpJGx3fHoTmKOtKuMWtQ", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/456a944e73c8110a13c746109a9b71393481588e", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/c0de23aeca99c8bf8537864c5216485464dd6db0", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/8c42af83179e899015ca48dcbc6b602756817307", + "width": 64 + } + ], + "name": "Francis Albert Sinatra & Antonio Carlos Jobim", + "release_date": "1967-03", + "release_date_precision": "month", + "total_tracks": 10, + "type": "album", + "uri": "spotify:album:0LYpJGx3fHoTmKOtKuMWtQ" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/3pO5VjZ4wOHCMBXOvbMISG" + }, + "href": "https://api.spotify.com/v1/artists/3pO5VjZ4wOHCMBXOvbMISG", + "id": "3pO5VjZ4wOHCMBXOvbMISG", + "name": "Antônio Carlos Jobim", + "type": "artist", + "uri": "spotify:artist:3pO5VjZ4wOHCMBXOvbMISG" + }, + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/1Mxqyy3pSjf8kZZL4QVxS0" + }, + "href": "https://api.spotify.com/v1/artists/1Mxqyy3pSjf8kZZL4QVxS0", + "id": "1Mxqyy3pSjf8kZZL4QVxS0", + "name": "Frank Sinatra", + "type": "artist", + "uri": "spotify:artist:1Mxqyy3pSjf8kZZL4QVxS0" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 194573, + "explicit": false, + "external_ids": { + "isrc": "USRH10800977" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/1id6N5Y8662aKNlmR5eM92" + }, + "href": "https://api.spotify.com/v1/tracks/1id6N5Y8662aKNlmR5eM92", + "id": "1id6N5Y8662aKNlmR5eM92", + "is_local": false, + "name": "The Girl From Ipanema", + "popularity": 41, + "preview_url": null, + "track_number": 1, + "type": "track", + "uri": "spotify:track:1id6N5Y8662aKNlmR5eM92" + }, + { + "album": { + "album_type": "single", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/6diA719p2OaW6zQnXCbRO9" + }, + "href": "https://api.spotify.com/v1/artists/6diA719p2OaW6zQnXCbRO9", + "id": "6diA719p2OaW6zQnXCbRO9", + "name": "US Two", + "type": "artist", + "uri": "spotify:artist:6diA719p2OaW6zQnXCbRO9" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/3WDrrELIumy9JiB9aZN5x7" + }, + "href": "https://api.spotify.com/v1/albums/3WDrrELIumy9JiB9aZN5x7", + "id": "3WDrrELIumy9JiB9aZN5x7", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/2f70df07dec22d7639b59c617e4f5b55d5024b1f", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/75ee2cdedd8ff8f18c92f449f28208ddf4fc929a", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/66839307af1f3a32237bcc6e946030345a662546", + "width": 64 + } + ], + "name": "US Two", + "release_date": "2018-10-28", + "release_date_precision": "day", + "total_tracks": 3, + "type": "album", + "uri": "spotify:album:3WDrrELIumy9JiB9aZN5x7" + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/6diA719p2OaW6zQnXCbRO9" + }, + "href": "https://api.spotify.com/v1/artists/6diA719p2OaW6zQnXCbRO9", + "id": "6diA719p2OaW6zQnXCbRO9", + "name": "US Two", + "type": "artist", + "uri": "spotify:artist:6diA719p2OaW6zQnXCbRO9" + } + ], + "available_markets": [ + "AD", + "AE", + "AR", + "AT", + "AU", + "BE", + "BG", + "BH", + "BO", + "BR", + "CA", + "CH", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DZ", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GR", + "GT", + "HK", + "HN", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JO", + "JP", + "KW", + "LB", + "LI", + "LT", + "LU", + "LV", + "MA", + "MC", + "MT", + "MX", + "MY", + "NI", + "NL", + "NO", + "NZ", + "OM", + "PA", + "PE", + "PH", + "PL", + "PS", + "PT", + "PY", + "QA", + "RO", + "SA", + "SE", + "SG", + "SK", + "SV", + "TH", + "TN", + "TR", + "TW", + "US", + "UY", + "VN", + "ZA" + ], + "disc_number": 1, + "duration_ms": 188194, + "explicit": false, + "external_ids": { + "isrc": "SE5IB1800990" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/1hOQ2MFPcuNupbsb8Xba1t" + }, + "href": "https://api.spotify.com/v1/tracks/1hOQ2MFPcuNupbsb8Xba1t", + "id": "1hOQ2MFPcuNupbsb8Xba1t", + "is_local": false, + "name": "Girl From Ipanema", + "popularity": 40, + "preview_url": "https://p.scdn.co/mp3-preview/37dc00a141411ad4448b7cede2248d91637dbcf4?cid=774b29d4f13844c495f206cafdad9c86", + "track_number": 2, + "type": "track", + "uri": "spotify:track:1hOQ2MFPcuNupbsb8Xba1t" + } + ], + "limit": 20, + "next": "https://api.spotify.com/v1/search?query=ipanema&type=track&market=US&offset=20&limit=20", + "offset": 0, + "previous": null, + "total": 8707 + } +}