Add Soundcloud Go+ support #1

Merged
h merged 2 commits from a/lucida:main into main 2024-02-11 02:10:30 -05:00
3 changed files with 36 additions and 19 deletions

View file

@ -9,6 +9,7 @@ export const DEFAULT_HEADERS = {
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'Sec-GPC': '1',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0'
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36'
}
export const SC_VERSION = '1686222762'

View file

@ -1,4 +1,4 @@
import fetch from 'node-fetch'
import fetch, { HeadersInit } from 'node-fetch'
import { DEFAULT_HEADERS, SC_VERSION } from './constants.js'
import { Streamer, SearchResults, GetByUrlResponse, GetStreamResponse } from '../../types.js'
import {
@ -12,12 +12,16 @@ import {
ScClient
} from './parse.js'
function headers(token?: string): HeadersInit {
function headers(oauthToken?: string | undefined): HeadersInit {
const headers: HeadersInit = DEFAULT_HEADERS
if (token) headers['Authentication'] = 'OAuth' + token
if (oauthToken) headers['Authorization'] = 'OAuth ' + oauthToken
return headers
}
interface SoundcloudOptions {
oauthToken?: string
}
interface SoundcloudTranscoding {
url: string
preset: string
@ -33,10 +37,11 @@ interface SoundcloudTranscoding {
export default class Soundcloud implements Streamer {
hostnames = ['soundcloud.com', 'm.soundcloud.com', 'www.soundcloud.com']
oauthToken?: string
token?: string
constructor(options: SoundcloudOptions) {
this.oauthToken = options.oauthToken
}
async search(query: string, limit = 20): Promise<SearchResults> {
const client = await this.#getClient()
if (this.oauthToken) client.id = this.oauthToken
const response = await fetch(
this.#formatURL(
@ -45,7 +50,7 @@ export default class Soundcloud implements Streamer {
)}&app_locale=en&limit=${limit}`,
client
),
{ method: 'get', headers: headers(this.token) }
{ method: 'get', headers: headers(this.oauthToken) }
)
if (!response.ok) {
const errMsg = await response.text()
@ -80,7 +85,7 @@ export default class Soundcloud implements Streamer {
const response = await (
await fetch(`https://soundcloud.com/`, {
method: 'get',
headers: headers(this.token)
headers: headers()
})
).text()
@ -123,10 +128,9 @@ export default class Soundcloud implements Streamer {
const type = this.getTypeFromUrl(url)
const client = await this.#getClient()
if (this.oauthToken) client.id = this.oauthToken
// getting the IDs and track authorization
const html = await (await fetch(url, { method: 'get', headers: headers(this.token) })).text()
const html = await (await fetch(url, { method: 'get', headers: headers() })).text()
switch (type) {
case 'track': {
@ -135,13 +139,11 @@ export default class Soundcloud implements Streamer {
?.split(`"id":`)?.[1]
?.split(',')?.[0]
const trackAuthorization = html.split(`"track_authorization":"`)[1].split(`"`)[0]
const api = JSON.parse(
await (
await fetch(
this.#formatURL(`https://api-v2.soundcloud.com/tracks?ids=${trackId}`, client),
{ method: 'get', headers: headers(this.token) }
{ method: 'get', headers: headers(this.oauthToken) }
)
).text()
)[0]
@ -149,7 +151,12 @@ export default class Soundcloud implements Streamer {
return {
type: 'track',
getStream: async () => {
return await getStream(api.media.transcodings, trackAuthorization, client)
return await getStream(
api.media.transcodings,
api.track_authorization,
client,
this.oauthToken
)
},
metadata: await parseTrack(api)
}
@ -206,7 +213,7 @@ export default class Soundcloud implements Streamer {
await (
await fetch(this.#formatURL(`https://api-v2.soundcloud.com/tracks?ids=${id}`, client), {
method: 'get',
headers: headers(this.token)
headers: headers(this.oauthToken)
})
).text()
)[0]
@ -239,14 +246,18 @@ async function fetchKey(response: string) {
async function getStream(
transcodings: Array<SoundcloudTranscoding>,
trackAuth: string,
client: ScClient
client: ScClient,
oauthToken?: string | undefined
): Promise<GetStreamResponse> {
const progressive = transcodings.filter((x) => x.format?.protocol == 'progressive')[0]
const streamUrlResp = await fetch(
`${progressive.url}?client_id=${client.id}&track_authorization=${trackAuth}`
`${progressive.url}?client_id=${client.id}&track_authorization=${trackAuth}`,
{
headers: headers(oauthToken)
}
)
const { url } = <{ url: string }>await streamUrlResp.json()
const streamResp = await fetch(url)
const json = <{ url: string }>await streamUrlResp.json()
const streamResp = await fetch(json.url)
return {
mimeType: progressive.format.mime_type,
sizeBytes: parseInt(<string>streamResp.headers.get('Content-Length')),

View file

@ -27,6 +27,11 @@ export interface RawArtist {
kind: 'user'
}
export interface Headers {
Authorization?: string
'User-Agent': string
}
export function parseArtist(raw: RawArtist): Artist {
const artist: Artist = {
id: raw.id,