Skip to content

Commit

Permalink
fix(QueryResolver): follow known redirect uri
Browse files Browse the repository at this point in the history
  • Loading branch information
twlite committed Oct 14, 2023
1 parent 5b7c71c commit f7b0e66
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 1 deletion.
14 changes: 14 additions & 0 deletions packages/discord-player/__test__/QueryResolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,18 @@ describe('QueryResolver', () => {
const query = 'https://example.com/music';
expect(qr.resolve(query).type).toBe(QueryType.ARBITRARY);
});

it('should resolve redirected url', async () => {
const query = 'https://spotify.link/QCGEhSsnuDb';
const result = await qr.preResolve(query);

expect(result).toMatch(qr.regex.spotifySongRegex);
});

it('should resolve invalid url in preResolve', async () => {
const query = 'query boi';
const result = await qr.preResolve(query);

expect(result).toBe(query);
});
});
3 changes: 2 additions & 1 deletion packages/discord-player/src/Player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,9 @@ export class Player extends PlayerEventsEmitter<PlayerEvents> {

if (this.hasDebugger) this.debug(`Search engine set to ${options.searchEngine}, fallback search engine set to ${options.fallbackSearchEngine}`);

const redirected = await QueryResolver.preResolve(searchQuery);
const { type: queryType, query } =
options.searchEngine === QueryType.AUTO ? QueryResolver.resolve(searchQuery, options.fallbackSearchEngine) : ({ type: options.searchEngine, query: searchQuery } as ResolvedQuery);
options.searchEngine === QueryType.AUTO ? QueryResolver.resolve(redirected, options.fallbackSearchEngine) : ({ type: options.searchEngine, query: redirected } as ResolvedQuery);

if (this.hasDebugger) this.debug(`Query type identified as ${queryType}`);

Expand Down
41 changes: 41 additions & 0 deletions packages/discord-player/src/utils/QueryResolver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { QueryType } from '../types/types';
import { TypeUtil } from './TypeUtil';
import { Exceptions } from '../errors';
import { fetch } from 'undici';

// #region scary things below *sigh*
const spotifySongRegex = /^https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(intl-([a-z]|[A-Z])+\/)?(?:track\/|\?uri=spotify:track:)((\w|-){22})(\?si=.+)?$/;
Expand Down Expand Up @@ -28,6 +29,11 @@ const DomainsMap = {
AppleMusic: ['music.apple.com']
};

// prettier-ignore
const redirectDomains = new Set([
/^https?:\/\/spotify.link\/[A-Za-z0-9]+$/,
]);

export interface ResolvedQuery {
type: (typeof QueryType)[keyof typeof QueryType];
query: string;
Expand Down Expand Up @@ -56,6 +62,41 @@ class QueryResolver {
};
}

/**
* Pre-resolve redirect urls
*/
static async preResolve(query: string, maxDepth = 5): Promise<string> {
if (!TypeUtil.isString(query)) throw Exceptions.ERR_INVALID_ARG_TYPE(query, 'string', typeof query);

for (const domain of redirectDomains) {
if (domain.test(query)) {
try {
const res = await fetch(query, {
method: 'GET',
redirect: 'follow'
});

if (!res.ok) break;

// spotify does not "redirect", it returns a page with js that redirects
if (/^https?:\/\/spotify.app.link\/(.+)$/.test(res.url)) {
const body = await res.text();
const target = body.split('window.top.location = validateProtocol("')[1].split('?si=')[0];

if (!target) break;

return target;
}
return maxDepth < 1 ? res.url : this.preResolve(res.url, maxDepth - 1);
} catch {
break;
}
}
}

return query;
}

/**
* Resolves the given search query
* @param {string} query The query
Expand Down

0 comments on commit f7b0e66

Please sign in to comment.