mirror of
https://codeberg.org/lucida/lucida.git
synced 2026-11-14 23:57:27 -05:00
add account checking to lucida, bugfix soundcloud
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "lucida",
|
"name": "lucida",
|
||||||
"version": "2.0.0-11",
|
"version": "2.0.0-12",
|
||||||
"description": "A modular music downloader tool",
|
"description": "A modular music downloader tool",
|
||||||
"main": "build/index.js",
|
"main": "build/index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"license": "OQL",
|
"license": "OQL",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"image-size": "^1.1.1",
|
"image-size": "^1.1.1",
|
||||||
"librespot": "^0.2.0",
|
"librespot": "^0.2.3",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"xmldom-qsa": "^1.1.3"
|
"xmldom-qsa": "^1.1.3"
|
||||||
},
|
},
|
||||||
|
|||||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -12,8 +12,8 @@ importers:
|
|||||||
specifier: ^1.1.1
|
specifier: ^1.1.1
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
librespot:
|
librespot:
|
||||||
specifier: ^0.2.0
|
specifier: ^0.2.3
|
||||||
version: 0.2.0
|
version: 0.2.3
|
||||||
node-fetch:
|
node-fetch:
|
||||||
specifier: ^3.3.2
|
specifier: ^3.3.2
|
||||||
version: 3.3.2
|
version: 3.3.2
|
||||||
@@ -26,7 +26,7 @@ importers:
|
|||||||
version: 20.11.17
|
version: 20.11.17
|
||||||
'@typescript-eslint/eslint-plugin':
|
'@typescript-eslint/eslint-plugin':
|
||||||
specifier: ^6.21.0
|
specifier: ^6.21.0
|
||||||
version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
|
version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3)
|
||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
specifier: ^6.21.0
|
specifier: ^6.21.0
|
||||||
version: 6.21.0(eslint@8.56.0)(typescript@5.3.3)
|
version: 6.21.0(eslint@8.56.0)(typescript@5.3.3)
|
||||||
@@ -462,8 +462,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
||||||
librespot@0.2.0:
|
librespot@0.2.3:
|
||||||
resolution: {integrity: sha512-v8k10GLVIdinAhYExnvDwkpXCXAp9VnNijcEtt9eLQxT8gd2Yym2xlUUppL0Lb3oAYz5/+Q3aP3H73dipvtumA==}
|
resolution: {integrity: sha512-EEGfAqClwLcfwiLvYiXbQyeLqk2Ownb9a+EuvsQwuuRKGbDxpcwlIgZBkU89Q1oLFmLKoY5rf+zgmOFTUXoTUA==}
|
||||||
|
|
||||||
locate-path@6.0.0:
|
locate-path@6.0.0:
|
||||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||||
@@ -766,7 +766,7 @@ snapshots:
|
|||||||
|
|
||||||
'@types/semver@7.5.6': {}
|
'@types/semver@7.5.6': {}
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)':
|
'@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/regexpp': 4.10.0
|
'@eslint-community/regexpp': 4.10.0
|
||||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
|
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
|
||||||
@@ -781,6 +781,7 @@ snapshots:
|
|||||||
natural-compare: 1.4.0
|
natural-compare: 1.4.0
|
||||||
semver: 7.6.0
|
semver: 7.6.0
|
||||||
ts-api-utils: 1.2.1(typescript@5.3.3)
|
ts-api-utils: 1.2.1(typescript@5.3.3)
|
||||||
|
optionalDependencies:
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -793,6 +794,7 @@ snapshots:
|
|||||||
'@typescript-eslint/visitor-keys': 6.21.0
|
'@typescript-eslint/visitor-keys': 6.21.0
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
|
optionalDependencies:
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -809,6 +811,7 @@ snapshots:
|
|||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
ts-api-utils: 1.2.1(typescript@5.3.3)
|
ts-api-utils: 1.2.1(typescript@5.3.3)
|
||||||
|
optionalDependencies:
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -825,6 +828,7 @@ snapshots:
|
|||||||
minimatch: 9.0.3
|
minimatch: 9.0.3
|
||||||
semver: 7.6.0
|
semver: 7.6.0
|
||||||
ts-api-utils: 1.2.1(typescript@5.3.3)
|
ts-api-utils: 1.2.1(typescript@5.3.3)
|
||||||
|
optionalDependencies:
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -1138,7 +1142,7 @@ snapshots:
|
|||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
|
|
||||||
librespot@0.2.0:
|
librespot@0.2.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-xml-parser: 4.3.4
|
fast-xml-parser: 4.3.4
|
||||||
protobufjs: 7.2.6
|
protobufjs: 7.2.6
|
||||||
|
|||||||
@@ -28,6 +28,28 @@ interface QobuzOptions {
|
|||||||
token?: string
|
token?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LoginResponse {
|
||||||
|
user: {
|
||||||
|
id: number
|
||||||
|
display_name: string
|
||||||
|
language_code: string
|
||||||
|
zone: string,
|
||||||
|
store: string
|
||||||
|
country: string
|
||||||
|
creation_date: string
|
||||||
|
credential: {
|
||||||
|
parameters: {
|
||||||
|
lossy_streaming: boolean
|
||||||
|
lossless_streaming: boolean
|
||||||
|
hires_streaming: boolean
|
||||||
|
}
|
||||||
|
label: string
|
||||||
|
source: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user_auth_token: string
|
||||||
|
}
|
||||||
|
|
||||||
export default class Qobuz implements StreamerWithLogin {
|
export default class Qobuz implements StreamerWithLogin {
|
||||||
hostnames = ['play.qobuz.com', 'open.qobuz.com', 'www.qobuz.com', 'qobuz.com']
|
hostnames = ['play.qobuz.com', 'open.qobuz.com', 'www.qobuz.com', 'qobuz.com']
|
||||||
token?: string
|
token?: string
|
||||||
@@ -100,9 +122,6 @@ export default class Qobuz implements StreamerWithLogin {
|
|||||||
app_id: this.appId
|
app_id: this.appId
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoginResponse {
|
|
||||||
user_auth_token: string
|
|
||||||
}
|
|
||||||
const loginResponse = <LoginResponse>await this.#getSigned('user/login', params)
|
const loginResponse = <LoginResponse>await this.#getSigned('user/login', params)
|
||||||
this.token = loginResponse.user_auth_token
|
this.token = loginResponse.user_auth_token
|
||||||
}
|
}
|
||||||
@@ -148,7 +167,7 @@ export default class Qobuz implements StreamerWithLogin {
|
|||||||
await this.#getSigned('track/getFileUrl', params)
|
await this.#getSigned('track/getFileUrl', params)
|
||||||
)
|
)
|
||||||
if (trackFileResponse.sample == true)
|
if (trackFileResponse.sample == true)
|
||||||
throw new Error('Could not get non-sample file. Check your token.')
|
throw new Error(`Could not get non-sample file. Make sure the track isn't download-only.`)
|
||||||
const streamResponse = await fetch(trackFileResponse.url)
|
const streamResponse = await fetch(trackFileResponse.url)
|
||||||
return {
|
return {
|
||||||
mimeType: trackFileResponse.mime_type,
|
mimeType: trackFileResponse.mime_type,
|
||||||
@@ -252,4 +271,14 @@ export default class Qobuz implements StreamerWithLogin {
|
|||||||
throw new Error('URL unrecognised')
|
throw new Error('URL unrecognised')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async getAccountInfo(): Promise<StreamerAccount> {
|
||||||
|
const loginResponse = <LoginResponse>await this.#getSigned('user/login', {extra: 'partner', device_manufacturer_id: 'undefined', app_id: this.appId})
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
premium: loginResponse.user.credential.parameters.hires_streaming,
|
||||||
|
country: loginResponse.user.country,
|
||||||
|
explicit: true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import {
|
|||||||
Streamer,
|
Streamer,
|
||||||
SearchResults,
|
SearchResults,
|
||||||
GetByUrlResponse,
|
GetByUrlResponse,
|
||||||
GetStreamResponse
|
GetStreamResponse,
|
||||||
|
StreamerAccount,
|
||||||
|
TrackGetByUrlResponse
|
||||||
} from '../../types.js'
|
} from '../../types.js'
|
||||||
import {
|
import {
|
||||||
parseAlbum,
|
parseAlbum,
|
||||||
@@ -118,15 +120,14 @@ export default class Soundcloud implements Streamer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#formatURL(og: string, client: ScClient): string {
|
#formatURL(og: string, client: ScClient): string {
|
||||||
let url = og
|
|
||||||
const parsed = new URL(og)
|
const parsed = new URL(og)
|
||||||
|
|
||||||
if (!parsed.searchParams.get('user_id')) url = url + `&user_id=${client.anonId}`
|
if (client.anonId) parsed.searchParams.append('user_id', client.anonId)
|
||||||
if (!parsed.searchParams.get('client_id')) url = url + `&client_id=${client.id}`
|
if (client.id) parsed.searchParams.append('client_id', client.id)
|
||||||
if (!parsed.searchParams.get('app_version')) url = url + `&app_version=${client.version}`
|
if (client.version) parsed.searchParams.append('app_version', client.version)
|
||||||
if (!parsed.searchParams.get('app_locale')) url = url + `&app_locale=en`
|
if (!parsed.searchParams.get('app_locale')) parsed.searchParams.append('app_locale', 'en')
|
||||||
|
|
||||||
return url
|
return parsed.href
|
||||||
}
|
}
|
||||||
|
|
||||||
async #getMetadata(url: string): Promise<GetByUrlResponse> {
|
async #getMetadata(url: string): Promise<GetByUrlResponse> {
|
||||||
@@ -141,8 +142,9 @@ export default class Soundcloud implements Streamer {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case 'track': {
|
case 'track': {
|
||||||
const trackId = html
|
const trackId = html
|
||||||
.split(`<link rel="alternate" href="android-app://com.soundcloud.android/soundcloud/sounds:`)?.[1]
|
.split(`"soundcloud://sounds:`)?.[1]
|
||||||
?.split(`">`)?.[0]
|
?.split(`">`)?.[0]
|
||||||
|
|
||||||
|
|
||||||
let naked = `https://api-v2.soundcloud.com/tracks/${trackId}`
|
let naked = `https://api-v2.soundcloud.com/tracks/${trackId}`
|
||||||
let path = new URL(url).pathname
|
let path = new URL(url).pathname
|
||||||
@@ -157,7 +159,7 @@ export default class Soundcloud implements Streamer {
|
|||||||
).text()
|
).text()
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return <TrackGetByUrlResponse>{
|
||||||
type: 'track',
|
type: 'track',
|
||||||
getStream: async (hq?: boolean) => {
|
getStream: async (hq?: boolean) => {
|
||||||
if (!hq) hq = false
|
if (!hq) hq = false
|
||||||
@@ -225,7 +227,7 @@ export default class Soundcloud implements Streamer {
|
|||||||
async #getRawTrackInfo(id: number | string, client: ScClient) {
|
async #getRawTrackInfo(id: number | string, client: ScClient) {
|
||||||
const api = JSON.parse(
|
const api = JSON.parse(
|
||||||
await (
|
await (
|
||||||
await fetch(this.#formatURL(`https://api-v2.soundcloud.com/tracks?ids=${id}`, client), {
|
await fetch(this.#formatURL(`hhttps://api-v2.soundcloud.com/tracks/${id}`, client), {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
headers: headers(this.oauthToken)
|
headers: headers(this.oauthToken)
|
||||||
})
|
})
|
||||||
@@ -234,6 +236,15 @@ export default class Soundcloud implements Streamer {
|
|||||||
|
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
async getAccountInfo(): Promise<StreamerAccount> {
|
||||||
|
const track = <TrackGetByUrlResponse>await this.getByUrl('https://soundcloud.com/ween/polka-dot-tail')
|
||||||
|
const stream = await track.getStream()
|
||||||
|
if (stream.mimeType.startsWith('audio/mp4')) {
|
||||||
|
stream.stream.unpipe()
|
||||||
|
return {valid: true, premium: true, explicit: true}
|
||||||
|
}
|
||||||
|
else return {valid: true, premium: false, explicit: true}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchKey(response: string) {
|
async function fetchKey(response: string) {
|
||||||
|
|||||||
@@ -108,9 +108,17 @@ class Spotify implements StreamerWithLogin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async getAccountInfo(): Promise<StreamerAccount> {
|
async getAccountInfo(): Promise<StreamerAccount> {
|
||||||
|
const info = await this.client.get.me()
|
||||||
|
|
||||||
|
let premium
|
||||||
|
if (info.plan == 'premium') premium = true
|
||||||
|
else premium = false
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valid: true,
|
valid: true,
|
||||||
premium: (await this.client.isPremium())
|
premium,
|
||||||
|
country: info.country,
|
||||||
|
explicit: info.allowExplicit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
disconnect() {
|
disconnect() {
|
||||||
|
|||||||
@@ -36,12 +36,13 @@ export function parseTrack(raw: SpotifyTrack) {
|
|||||||
durationMs: raw.durationMs,
|
durationMs: raw.durationMs,
|
||||||
}
|
}
|
||||||
if (raw.album) track.album = parseAlbum(raw.album)
|
if (raw.album) track.album = parseAlbum(raw.album)
|
||||||
//if (raw?.isrc) track.isrc = raw.isrc
|
if (raw?.isrc) track.isrc = raw.isrc
|
||||||
|
if (raw?.album?.availableMarkets) track.regions = raw.album.availableMarkets
|
||||||
return track
|
return track
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseAlbum(raw: SpotifyAlbum): Album {
|
export function parseAlbum(raw: SpotifyAlbum) {
|
||||||
return {
|
const album: Album = {
|
||||||
title: raw.name,
|
title: raw.name,
|
||||||
id: raw.id,
|
id: raw.id,
|
||||||
url: raw.externalUrl,
|
url: raw.externalUrl,
|
||||||
@@ -50,6 +51,10 @@ export function parseAlbum(raw: SpotifyAlbum): Album {
|
|||||||
coverArtwork: parseThumbnails(raw.coverArtwork),
|
coverArtwork: parseThumbnails(raw.coverArtwork),
|
||||||
artists: raw.artists.map((e) => parseArtist(e)),
|
artists: raw.artists.map((e) => parseArtist(e)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (raw.availableMarkets) album.regions = raw.availableMarkets
|
||||||
|
|
||||||
|
return album
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseEpisode(raw: SpotifyEpisode) {
|
export function parseEpisode(raw: SpotifyEpisode) {
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export const TIDAL_AUTH_BASE = 'https://auth.tidal.com/v1/'
|
export const TIDAL_AUTH_BASE = 'https://auth.tidal.com/v1/'
|
||||||
export const TIDAL_API_BASE = 'https://listen.tidal.com/v1/'
|
export const TIDAL_API_BASE = 'https://listen.tidal.com/v1/'
|
||||||
|
export const TIDAL_SUBSCRIPTION_BASE = 'https://api.tidal.com/v1/'
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import {
|
|||||||
GetStreamResponse,
|
GetStreamResponse,
|
||||||
SearchResults,
|
SearchResults,
|
||||||
Streamer,
|
Streamer,
|
||||||
Track
|
Track,
|
||||||
|
StreamerAccount
|
||||||
} from '../../types.js'
|
} from '../../types.js'
|
||||||
import { TIDAL_AUTH_BASE, TIDAL_API_BASE } from './constants.js'
|
import { TIDAL_AUTH_BASE, TIDAL_API_BASE, TIDAL_SUBSCRIPTION_BASE } from './constants.js'
|
||||||
import {
|
import {
|
||||||
Contributor,
|
Contributor,
|
||||||
RawAlbum,
|
RawAlbum,
|
||||||
@@ -40,6 +41,22 @@ interface LoginData {
|
|||||||
|
|
||||||
interface SessionData {
|
interface SessionData {
|
||||||
countryCode: string
|
countryCode: string
|
||||||
|
userId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubscriptionData {
|
||||||
|
startDate: string
|
||||||
|
validUntil: string
|
||||||
|
status: string
|
||||||
|
subscription: {
|
||||||
|
type: string
|
||||||
|
offlineGracePeriod: number
|
||||||
|
}
|
||||||
|
highestSoundQuality: string
|
||||||
|
premiumAccess: boolean
|
||||||
|
canGetTrial: boolean
|
||||||
|
paymentType: string,
|
||||||
|
paymentOverdue: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Tidal implements Streamer {
|
export default class Tidal implements Streamer {
|
||||||
@@ -49,6 +66,7 @@ export default class Tidal implements Streamer {
|
|||||||
refreshToken: string
|
refreshToken: string
|
||||||
expires: number
|
expires: number
|
||||||
countryCode: string
|
countryCode: string
|
||||||
|
userId: number | undefined
|
||||||
hostnames = ['tidal.com', 'www.tidal.com', 'listen.tidal.com']
|
hostnames = ['tidal.com', 'www.tidal.com', 'listen.tidal.com']
|
||||||
failedAuth = false
|
failedAuth = false
|
||||||
constructor(options: TidalOptions) {
|
constructor(options: TidalOptions) {
|
||||||
@@ -77,7 +95,7 @@ export default class Tidal implements Streamer {
|
|||||||
'User-Agent': 'TIDAL_ANDROID/1039 okhttp/3.14.9'
|
'User-Agent': 'TIDAL_ANDROID/1039 okhttp/3.14.9'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async #get(url: string, params: { [key: string]: string | number } = {}): Promise<unknown> {
|
async #get(url: string, params: { [key: string]: string | number } = {}, base: string = TIDAL_API_BASE): Promise<unknown> {
|
||||||
if (this.failedAuth) throw new Error(`Last request failed to authorize, get new tokens`)
|
if (this.failedAuth) throw new Error(`Last request failed to authorize, get new tokens`)
|
||||||
if (Date.now() > this.expires) await this.refresh()
|
if (Date.now() > this.expires) await this.refresh()
|
||||||
if (!this.countryCode) await this.getCountryCode()
|
if (!this.countryCode) await this.getCountryCode()
|
||||||
@@ -88,7 +106,7 @@ export default class Tidal implements Streamer {
|
|||||||
if (typeof params[key] == 'number') params[key] = params[key].toString()
|
if (typeof params[key] == 'number') params[key] = params[key].toString()
|
||||||
}
|
}
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${TIDAL_API_BASE}${url}?${new URLSearchParams(<{ [key: string]: string }>params)}`,
|
`${base}${url}?${new URLSearchParams(<{ [key: string]: string }>params)}`,
|
||||||
{
|
{
|
||||||
headers: this.headers()
|
headers: this.headers()
|
||||||
}
|
}
|
||||||
@@ -127,6 +145,7 @@ export default class Tidal implements Streamer {
|
|||||||
if (sessionResponse.status != 200) return false
|
if (sessionResponse.status != 200) return false
|
||||||
const sessionData = <SessionData>await sessionResponse.json()
|
const sessionData = <SessionData>await sessionResponse.json()
|
||||||
this.countryCode = sessionData.countryCode
|
this.countryCode = sessionData.countryCode
|
||||||
|
this.userId = sessionData.userId
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
async getTokens() {
|
async getTokens() {
|
||||||
@@ -392,4 +411,15 @@ export default class Tidal implements Streamer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async getAccountInfo(): Promise<StreamerAccount> {
|
||||||
|
if (!this.userId) await this.getCountryCode()
|
||||||
|
const subscription = <SubscriptionData>await this.#get(`users/${this.userId}/subscription`, {}, TIDAL_SUBSCRIPTION_BASE)
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
premium: subscription.premiumAccess,
|
||||||
|
country: this.countryCode,
|
||||||
|
explicit: true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export interface Album {
|
|||||||
copyright?: string,
|
copyright?: string,
|
||||||
label?: string,
|
label?: string,
|
||||||
genre?: string[]
|
genre?: string[]
|
||||||
|
regions?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Track {
|
export interface Track {
|
||||||
@@ -52,9 +53,9 @@ export interface Track {
|
|||||||
album?: Album
|
album?: Album
|
||||||
durationMs?: number
|
durationMs?: number
|
||||||
coverArtwork?: CoverArtwork[]
|
coverArtwork?: CoverArtwork[]
|
||||||
regions?: Region[]
|
|
||||||
genres?: string[]
|
genres?: string[]
|
||||||
releaseDate?: Date
|
releaseDate?: Date
|
||||||
|
regions?: string[]
|
||||||
description?: string
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +135,7 @@ export interface StreamerAccount {
|
|||||||
valid: boolean
|
valid: boolean
|
||||||
premium?: boolean
|
premium?: boolean
|
||||||
country?: string
|
country?: string
|
||||||
|
explicit?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Streamer {
|
export interface Streamer {
|
||||||
@@ -144,7 +146,7 @@ export interface Streamer {
|
|||||||
| ((url: string) => Promise<GetByUrlResponse>)
|
| ((url: string) => Promise<GetByUrlResponse>)
|
||||||
| ((url: string, limit?: number) => Promise<GetByUrlResponse>)
|
| ((url: string, limit?: number) => Promise<GetByUrlResponse>)
|
||||||
disconnect?(): Promise<void>
|
disconnect?(): Promise<void>
|
||||||
getAccountInfo?(): Promise<StreamerAccount>
|
getAccountInfo(): Promise<StreamerAccount>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StreamerWithLogin extends Streamer {
|
export interface StreamerWithLogin extends Streamer {
|
||||||
|
|||||||
Reference in New Issue
Block a user