Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

spotify-url-info

Package Overview
Dependencies
Maintainers
2
Versions
59
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

spotify-url-info - npm Package Compare versions

Comparing version 3.0.0 to 3.0.1

readme.md

20

package.json

@@ -5,3 +5,3 @@ {

"homepage": "https://github.com/microlinkhq/spotify-url-info",
"version": "3.0.0",
"version": "3.0.1",
"main": "src/index.js",

@@ -16,18 +16,2 @@ "author": {

"email": "josefrancisco.verdu@gmail.com"
},
{
"name": "DaliborTrampota",
"email": "dalibor.trampota@gmail.com"
},
{
"name": "crxts",
"email": "49580728+crxts@users.noreply.github.com"
},
{
"name": "kaaax0815",
"email": "999999bst@gmail.com"
},
{
"name": "KeepSOBP",
"email": "keepsobp@naver.com"
}

@@ -61,5 +45,5 @@ ],

"conventional-github-releaser": "latest",
"cross-fetch": "latest",
"finepack": "latest",
"git-authors-cli": "latest",
"isomorphic-unfetch": "latest",
"nano-staged": "latest",

@@ -66,0 +50,0 @@ "npm-check-updates": "latest",

'use strict'
const spotifyURI = require('spotify-uri')
const { parse } = require('himalaya')
const SUPPORTED_TYPES = ['album', 'artist', 'episode', 'playlist', 'track']
const TYPE = {
ALBUM: 'album',
ARTIST: 'artist',
EPISODE: 'episode',
PLAYLIST: 'playlist',
TRACK: 'track'
}
const createGetData = fetch => (url, opts) => {
let parsedURL = {}
const SUPPORTED_TYPES = Object.values(TYPE)
try {
parsedURL = spotifyURI.parse(url)
} catch (error) {
return Promise.reject(new TypeError(`Couldn't parse '${url}' as valid URL`))
const createGetData = fetch => async (url, opts) => {
const parsedUrl = getParsedUrl(url)
const embedURL = spotifyURI.formatEmbedURL(parsedUrl)
const response = await fetch(embedURL, opts)
const text = await response.text()
const embed = parse(text)
const scripts = embed
.filter(e => e.tagName === 'html')[0]
.children.filter(e => e.tagName === 'body')[0]
.children.filter(e => e.tagName === 'script')
const resourceScript = scripts.filter(
e => e.attributes.findIndex(a => a.value === 'resource') !== -1
)
const hydrateScript = scripts.filter(
e => e.children[0] && /%22data%22%|"data":/.test(e.children[0].content)
)
if (resourceScript.length > 0) {
// found data in the older embed style
return JSON.parse(decodeURIComponent(resourceScript[0].children[0].content))
}
if (!parsedURL.type) {
return Promise.reject(
new TypeError(`Failed to parse '${url}' as Spotify URL`)
if (hydrateScript.length > 0) {
// found hydration data
// parsing via looking for { to be a little bit resistant to code changes
const scriptContent = hydrateScript[0].children[0].content.includes(
'%22data%22%'
)
? decodeURIComponent(hydrateScript[0].children[0].content)
: hydrateScript[0].children[0].content
return normalizeData(
JSON.parse(
'{' +
scriptContent
.split('{')
.slice(1)
.join('{')
.trim()
)
)
}
const embedURL = spotifyURI.formatEmbedURL(parsedURL)
throw new Error(
"Couldn't find any data in embed page that we know how to parse"
)
}
return fetch(embedURL, opts)
.then(res => res.text())
.then(parse)
.then(embed => {
const scripts = embed
.filter(e => e.tagName === 'html')[0]
.children.filter(e => e.tagName === 'body')[0]
.children.filter(e => e.tagName === 'script')
const resourceScript = scripts.filter(
e => e.attributes.findIndex(a => a.value === 'resource') !== -1
)
const hydrateScript = scripts.filter(
e => e.children[0] && /%22data%22%|"data":/.test(e.children[0].content)
)
function getParsedUrl (url) {
try {
const parsedURL = spotifyURI.parse(url)
if (!parsedURL.type) throw new TypeError()
return spotifyURI.formatEmbedURL(parsedURL)
} catch (_) {
throw new TypeError(`Couldn't parse '${url}' as valid URL`)
}
}
if (resourceScript.length > 0) {
// found data in the older embed style
return JSON.parse(
decodeURIComponent(resourceScript[0].children[0].content)
)
} else if (hydrateScript.length > 0) {
// found hydration data
// parsing via looking for { to be a little bit resistant to code changes
const scriptContent = hydrateScript[0].children[0].content.includes(
'%22data%22%'
)
? decodeURIComponent(hydrateScript[0].children[0].content)
: hydrateScript[0].children[0].content
const data = JSON.parse(
'{' +
scriptContent
.split('{')
.slice(1)
.join('{')
.trim()
).data
return data.entity ? data.entity : data
} else {
return Promise.reject(
new Error(
"Couldn't find any data in embed page that we know how to parse"
)
)
}
})
.then(sanityCheck)
function getImages (data) {
switch (data.type) {
case TYPE.TRACK:
return data.album.images
case TYPE.EPISODE:
return data.coverArt.sources
default:
return data.images
}
}
function toPreview (data) {
const track = getFirstTrack(data)
const images = data.type === 'track' ? data.album.images : data.images
const date = data.album ? data.album.release_date : data.release_date
function getDate (data) {
switch (data.type) {
case TYPE.TRACK:
return data.album.release_date
case TYPE.EPISODE:
return data.releaseDate.isoString
default:
return data.release_date
}
}
const artist = track.show
function getArtistTrack (track) {
return track.show
? track.show.publisher

@@ -86,5 +107,18 @@ : []

.join(' & ')
}
return Promise.resolve({
date,
function getLink (data) {
switch (data.type) {
case TYPE.EPISODE:
return data.sharingInfo.shareUrl
default:
return data.external_urls.spotify
}
}
function getPreview (data) {
const track = getFirstTrack(data)
return {
date: getDate(data),
title: data.name,

@@ -94,25 +128,20 @@ type: data.type,

description: data.description || undefined,
artist: artist || undefined,
image: images.reduce((a, b) => (a.width > b.width ? a : b)).url,
artist: getArtistTrack(track),
image: getImages(data).reduce((a, b) => (a.width > b.width ? a : b)).url,
audio: track.audio_preview_url || track.preview_url,
link: data.external_urls.spotify,
link: getLink(data),
embed: `https://embed.spotify.com/?uri=${data.uri}`
})
}
}
function getTracks (data) {
if (!data.tracks) {
// Is a track or a podcast episode
return Promise.resolve([data])
} else if (data.tracks.items) {
if (data.tracks.items[0].track) {
// Is a playlist
return Promise.resolve(data.tracks.items.map(t => t.track))
} else {
// Is an album
return Promise.resolve(data.tracks.items)
}
} else {
// Is an artist
return Promise.resolve(data.tracks)
switch (data.type) {
case TYPE.PLAYLIST:
return data.tracks.items.map(({ track }) => track)
case TYPE.ARTIST:
return data.tracks
case TYPE.ALBUM:
return data.tracks.items
default:
return [data]
}

@@ -123,36 +152,40 @@ }

switch (data.type) {
case 'track':
case TYPE.TRACK:
return data
case 'playlist':
case TYPE.PLAYLIST:
return data.tracks.items[0].track
case 'album':
case TYPE.ALBUM:
return data.tracks.items[0]
case 'artist':
case TYPE.ARTIST:
return data.tracks[0]
case 'episode':
case TYPE.EPISODE: {
const { podcast, audioPreview } = data
return {
artists: data.show.publisher.split(' and ').map(name => ({ name })),
name: data.show.name,
preview_url: data.audio_preview_url
artists: podcast.publisher.name.split(' and ').map(name => ({ name })),
name: podcast.name,
preview_url: audioPreview.url
}
default:
return data
}
}
}
function sanityCheck (data) {
function normalizeData ({ data }) {
data = data.entity ? data.entity : data
if (data.episode) {
data = data.episode
data.type = TYPE.EPISODE
}
if (!data || !data.type || !data.name) {
return Promise.reject(
new Error("Data doesn't seem to be of the right shape to parse")
)
throw new Error("Data doesn't seem to be of the right shape to parse")
}
if (!SUPPORTED_TYPES.includes(data.type)) {
return Promise.reject(
new Error(
`Not an ${SUPPORTED_TYPES.join(', ')}. Only these types can be parsed`
)
throw new Error(
`Not an ${SUPPORTED_TYPES.join(', ')}. Only these types can be parsed`
)
}
return Promise.resolve(data)
return data
}

@@ -164,5 +197,5 @@

getData,
getPreview: (url, opts) => getData(url, opts).then(toPreview),
getPreview: (url, opts) => getData(url, opts).then(getPreview),
getTracks: (url, opts) => getData(url, opts).then(getTracks)
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc