From 228a62208ac8ce32b1d6e28fd13dc383c34413c6 Mon Sep 17 00:00:00 2001 From: Aria Blue Date: Sun, 11 Feb 2024 01:55:41 -0500 Subject: [PATCH 1/2] add soundcloud go+ support --- src/streamers/soundcloud/constants.ts | 2 +- src/streamers/soundcloud/main.ts | 42 +++++++++++++++------------ src/streamers/soundcloud/parse.ts | 5 ++++ 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/streamers/soundcloud/constants.ts b/src/streamers/soundcloud/constants.ts index f32c9c8..ed695e7 100644 --- a/src/streamers/soundcloud/constants.ts +++ b/src/streamers/soundcloud/constants.ts @@ -9,6 +9,6 @@ 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' diff --git a/src/streamers/soundcloud/main.ts b/src/streamers/soundcloud/main.ts index 2ee5bea..46abc0f 100644 --- a/src/streamers/soundcloud/main.ts +++ b/src/streamers/soundcloud/main.ts @@ -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 { 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,7 @@ 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 +208,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 +241,18 @@ async function fetchKey(response: string) { async function getStream( transcodings: Array, trackAuth: string, - client: ScClient + client: ScClient, + oauthToken?: string | undefined ): Promise { 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(streamResp.headers.get('Content-Length')), diff --git a/src/streamers/soundcloud/parse.ts b/src/streamers/soundcloud/parse.ts index b56b4f6..d28dc74 100644 --- a/src/streamers/soundcloud/parse.ts +++ b/src/streamers/soundcloud/parse.ts @@ -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, From 1761fab9fa63183efef09e692be98e0ce76490a9 Mon Sep 17 00:00:00 2001 From: Aria Blue Date: Sun, 11 Feb 2024 01:58:04 -0500 Subject: [PATCH 2/2] lint --- src/streamers/soundcloud/constants.ts | 3 ++- src/streamers/soundcloud/main.ts | 11 ++++++++--- src/streamers/soundcloud/parse.ts | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/streamers/soundcloud/constants.ts b/src/streamers/soundcloud/constants.ts index ed695e7..a2b7588 100644 --- a/src/streamers/soundcloud/constants.ts +++ b/src/streamers/soundcloud/constants.ts @@ -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 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36' + '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' diff --git a/src/streamers/soundcloud/main.ts b/src/streamers/soundcloud/main.ts index 46abc0f..aef5198 100644 --- a/src/streamers/soundcloud/main.ts +++ b/src/streamers/soundcloud/main.ts @@ -14,7 +14,7 @@ import { function headers(oauthToken?: string | undefined): HeadersInit { const headers: HeadersInit = DEFAULT_HEADERS - if (oauthToken) headers['Authorization'] = 'OAuth ' + oauthToken; + if (oauthToken) headers['Authorization'] = 'OAuth ' + oauthToken return headers } @@ -38,7 +38,7 @@ export default class Soundcloud implements Streamer { hostnames = ['soundcloud.com', 'm.soundcloud.com', 'www.soundcloud.com'] oauthToken?: string constructor(options: SoundcloudOptions) { - this.oauthToken = options.oauthToken; + this.oauthToken = options.oauthToken } async search(query: string, limit = 20): Promise { const client = await this.#getClient() @@ -151,7 +151,12 @@ export default class Soundcloud implements Streamer { return { type: 'track', getStream: async () => { - return await getStream(api.media.transcodings, api.track_authorization, client, this.oauthToken) + return await getStream( + api.media.transcodings, + api.track_authorization, + client, + this.oauthToken + ) }, metadata: await parseTrack(api) } diff --git a/src/streamers/soundcloud/parse.ts b/src/streamers/soundcloud/parse.ts index d28dc74..19f71d0 100644 --- a/src/streamers/soundcloud/parse.ts +++ b/src/streamers/soundcloud/parse.ts @@ -29,7 +29,7 @@ export interface RawArtist { export interface Headers { Authorization?: string - "User-Agent": string + 'User-Agent': string } export function parseArtist(raw: RawArtist): Artist {