Skip to content
This repository has been archived by the owner on Mar 26, 2024. It is now read-only.

Commit

Permalink
feat: add resolve.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
vaaski committed Feb 11, 2021
1 parent 457c1ce commit 8ac5b5a
Show file tree
Hide file tree
Showing 12 changed files with 575 additions and 687 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
SOUNDCLOUD_CLIENT_ID=
SOUNDCLOUD_CLIENT_ID_V1=
SOUNDCLOUD_CLIENT_ID_V2=
SOUNDCLOUD_OAUTH_TOKEN=
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.env
node_modules
lib
lib
demo_out.json
3 changes: 1 addition & 2 deletions .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
"printWidth": 90,
"tabWidth": 2,
"useTabs": false,
"singleQuote": false,
"endOfLine": "crlf"
"singleQuote": false
}
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cSpell.words": [
"urlify"
]
}
35 changes: 35 additions & 0 deletions demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { resolve } from "./src"
import { writeFileSync } from "fs"

const byteLength = (input: any) => Buffer.byteLength(JSON.stringify(input), "utf8")
const formatSize = (bytes: number, decimals = 2) => {
if (bytes === 0) return "0 Bytes"

const k = 1024
const dm = decimals < 0 ? 0 : decimals
const sizes = ["Bytes", "KB", "MB", "GB"]

const i = Math.floor(Math.log(bytes) / Math.log(k))

return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]
}

const client_id = process.env.SOUNDCLOUD_CLIENT_ID_V1 || "client_id"
const examplePlaylist = "https://soundcloud.com/vaaski/sets/rrrrrrrrr"
const exampleUser = "https://soundcloud.com/vaaski"
const exampleTrack = "https://soundcloud.com/vaaski/slow-night"

!(async () => {
const startTime = Date.now()

const out = await resolve(exampleUser)
// const out = await resolve(exampleTrack)
// const out = await resolve(examplePlaylist)
// const out = await resolve.browser(exampleUser, client_id)
// const out = await resolve.browser(exampleTrack, client_id)
// const out = await resolve.browser(examplePlaylist, client_id)

console.log(`fetched ${formatSize(byteLength(out))} in ${Date.now() - startTime}ms`)

writeFileSync("demo_out.json", JSON.stringify(out, null, 2))
})()
695 changes: 21 additions & 674 deletions license

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions nodemon.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"watch": ["src"],
"watch": ["src", "types", "demo.ts"],
"ext": "ts, json",
"exec": "npx ts-node ./src/index.ts",
"exec": "npx ts-node -r dotenv/config ./demo.ts",
"events": {
"start": "node -e console.clear()"
}
Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,11 @@
}
}
],
"@typescript-eslint/no-explicit-any": [
"off"
],
"@typescript-eslint/indent": [
"error",
2
]
],
"@typescript-eslint/no-explicit-any": "off"
},
"overrides": [
{
Expand Down
4 changes: 1 addition & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
import ky from "ky-universal"

ky("https://httpbin.org/anything").json().then(console.log)
export * from "./resolve"
36 changes: 36 additions & 0 deletions src/resolve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { APIv1Resolve, ScrapeResolve } from "../types/resolve"

import { APIv1, at, urlify } from "./util"
import ky from "ky-universal"

const htmlDataReg = /(\[{"id")(.*?)(?=\);)/i

/**
* Resolve a track, user or playlist using web scraping.
* This doesn't work in browsers, as CORS is disabled.
* In browsers you have to use `resolve.browser` instead.
* @param url
*/
export const resolve = async (url: string): Promise<ScrapeResolve> => {
const html = await ky(urlify(url)).text()
const [match] = html.match(htmlDataReg) || []
const parsed = JSON.parse(match)
const { data } = at(parsed, -1) || at(parsed, -2)

return data[0] as ScrapeResolve
}

/**
* Resolve a track, user or playlist using the public APIv1.
* This works in browsers, as CORS is enabled.
* @param url the url to resolve
* @param client_id a APIv1 client_id
*/
resolve.browser = async (url: string, client_id: string): Promise<APIv1Resolve> => {
url = urlify(url)

const searchParams = { url, client_id }
const data = await ky(`${APIv1}/resolve`, { searchParams }).json()

return data as APIv1Resolve
}
12 changes: 12 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// cspell:ignore urlify

export const scrapeURL = "https://soundcloud.com"
export const APIv1 = "https://api.soundcloud.com"
export const APIv2 = "https://api-v2.soundcloud.com"
export const urlify = (url: string, base = scrapeURL): string =>
new URL(url, base).toString()

export const at = <T>(arr: Array<T>, pos: number): T => {
if (pos >= 0) return arr[pos]
return arr[arr.length + pos]
}
Loading

0 comments on commit 8ac5b5a

Please sign in to comment.