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

Commit

Permalink
feat: add user.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
vaaski committed Feb 12, 2021
1 parent 3469e2a commit cd77a2a
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"semi": false,
"trailingComma": "es5",
"arrowParens": "avoid",
"printWidth": 90,
"printWidth": 95,
"tabWidth": 2,
"useTabs": false,
"singleQuote": false,
Expand Down
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cSpell.words": [
"Playlistv",
"Userv",
"lcov",
"npmrc",
"sndcdn",
Expand Down
9 changes: 6 additions & 3 deletions demo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { playlist, resolve } from "./src"
import { playlist, resolve, user } from "./src"
import { writeFileSync } from "fs"
import { getClientIDv2 } from "./src/util"
import { join } from "path"
Expand All @@ -22,12 +22,15 @@ const client_id2 = process.env.SOUNDCLOUD_CLIENT_ID_V2 || "client_id"
const examplePlaylistURL = process.env.EXAMPLE_PLAYLIST_URL || ""
const examplePlaylistID = Number(process.env.EXAMPLE_PLAYLIST_ID) || 0
const exampleUserURL = process.env.EXAMPLE_USER_URL || ""
const exampleUserID = Number(process.env.EXAMPLE_USER_ID) || 0
const exampleTrackURL = process.env.EXAMPLE_TRACK_URL || ""

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

const out = await playlist(examplePlaylistURL)
const out = await user(exampleUserID)
// const out = await user(exampleUserURL)
// const out = await playlist(examplePlaylistURL)
// const out = await playlist(examplePlaylistID)
// const out = await getClientIDv2()
// const out = await resolve(exampleUserURL)
Expand All @@ -38,7 +41,7 @@ const exampleTrackURL = process.env.EXAMPLE_TRACK_URL || ""
// const out = await resolve.browser(examplePlaylistURL, client_id)

console.log(
`fetched ${formatSize(byteLength(out))} in ${Date.now() - startTime}ms\n` +
`fetched ${formatSize(byteLength(out))} of data in ${Date.now() - startTime}ms\n` +
`see ${join(__dirname, "demo_out.json")}`
)

Expand Down
5 changes: 3 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import resolve from "./resolve"
import playlist from "./playlist"
import user from "./user"

export default { resolve, playlist }
export { resolve, playlist }
export default { resolve, playlist, user }
export { resolve, playlist, user }
18 changes: 9 additions & 9 deletions src/playlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { APIv2, getClientIDv2, scrapeData, ScrapeIDs, urlify } from "./util"
* @param id A playlist ID
* @param client_id client_id for APIv2
*/
const byID = async (id: number, client_id: ClientIDv2): Promise<Playlistv2> => {
const byID = async (id: number, client_id: ClientIDv2) => {
const url = urlify(`playlists/${id}`, APIv2)
const searchParams = { client_id }

Expand All @@ -21,13 +21,13 @@ const byID = async (id: number, client_id: ClientIDv2): Promise<Playlistv2> => {
* Get a playlist using the APIv2 with a playlist URL
* @param url A playlist URL
*/
const byURL = async (url: string): Promise<Playlistv2> => {
const byURL = async (url: string) => {
const scraped = await scrapeData(urlify(url))
const playlistData = scraped.find(({ id }) => id === ScrapeIDs.playlist)
if (!playlistData) throw new Error("No playlist data found.")

const { data } = playlistData
return data[0] as Playlistv2
const [data] = playlistData.data
return data as Playlistv2
}

/**
Expand All @@ -36,15 +36,15 @@ const byURL = async (url: string): Promise<Playlistv2> => {
* If you use a playlist ID, you can provide a v2 client_id (recommended).
*
* Uses `util.getClientIDv2` to find a client_id if none is provided.
* @param source A playlist URL or ID
* @param identifier A playlist URL or ID
* @param client_id Optional.
*/
const playlist = async (source: URLorID, client_id?: string): Promise<Playlistv2> => {
if (typeof source === "string") return await byURL(source)
const playlist = async (identifier: URLorID, client_id?: ClientIDv2): Promise<Playlistv2> => {
if (typeof identifier === "string") return await byURL(identifier)

if (typeof source === "number") {
if (typeof identifier === "number") {
if (!client_id) client_id = await getClientIDv2()
return await byID(source, client_id)
return await byID(identifier, client_id)
}

throw new Error("Source must be a string (URL) or a number (ID)")
Expand Down
38 changes: 38 additions & 0 deletions src/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { ClientIDv2, URLorID } from "../types"
import type { Userv2 } from "../types/user"

import ky from "ky-universal"
import { APIv2, getClientIDv2, scrapeData, ScrapeIDs, urlify } from "./util"

const byID = async (id: number, client_id: ClientIDv2) => {
const url = urlify(`users/${id}`, APIv2)
const searchParams = { client_id }

const data = await ky(url, { searchParams }).json()
return data as Userv2
}

const byURL = async (url: string) => {
const scraped = await scrapeData(urlify(url))
const userData = scraped.find(({ id }) => id === ScrapeIDs.user)
if (!userData) throw new Error("No user data found.")

const [data] = userData.data
return data as Userv2
}

const user = async (identifier: URLorID, client_id?: ClientIDv2): Promise<any> => {
if (typeof identifier === "string") return await byURL(identifier)

if (typeof identifier === "number") {
if (!client_id) client_id = await getClientIDv2()
return await byID(identifier, client_id)
}

throw new Error("Source must be a string (URL) or a number (ID)")
}

user.id = byID
user.url = byURL

export default user
44 changes: 44 additions & 0 deletions tests/user.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import test from "ava"

import { user } from "../src"
import { getClientIDv2 } from "../src/util"

const exampleUserURL = process.env.EXAMPLE_USER_URL || ""
const exampleUserID = Number(process.env.EXAMPLE_USER_ID) || 0

let client_id2: string

test.before(async () => {
client_id2 = await getClientIDv2()
})

test("get user using URL", async t => {
const data = await user(exampleUserURL)
t.is(data.kind, "user")
t.truthy(data.id)
})

test("get user using ID", async t => {
const data = await user(exampleUserID)
t.is(data.kind, "user")
t.truthy(data.id)
})

test("get user using ID and client_id", async t => {
const data = await user(exampleUserID, client_id2)
t.is(data.kind, "user")
t.truthy(data.id)
})

test("get user throws using wrong input", async t => {
// @ts-expect-error intentionally wrong input
await t.throwsAsync(user([]))
})

test("get user by URL throws when not found", async t => {
await t.throwsAsync(user(""))
})

test("get user by ID throws when not found", async t => {
await t.throwsAsync(user(0, client_id2))
})
60 changes: 60 additions & 0 deletions types/user.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export interface Userv2 {
avatar_url: string
city: string
comments_count: number
country_code: null | string
created_at: Date
creator_subscriptions: CreatorSubscription[]
creator_subscription: CreatorSubscription
description: string
followers_count: number
followings_count: number
first_name: string
full_name: string
groups_count: number
id: number
kind: string
last_modified: Date
last_name: string
likes_count: number
playlist_likes_count: number
permalink: string
permalink_url: string
playlist_count: number
reposts_count: null
track_count: number
uri: string
urn: string
username: string
verified: boolean
visuals: Visuals
badges: Badges
url?: string
}

export interface Badges {
pro: boolean
pro_unlimited: boolean
verified: boolean
}

export interface CreatorSubscription {
product: Product
}

export interface Product {
id: string
}

export interface Visuals {
urn: string
enabled: boolean
visuals: Visual[]
tracking: null
}

export interface Visual {
urn: string
entry_time: number
visual_url: string
}

0 comments on commit cd77a2a

Please sign in to comment.