Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Spotify shorthand playlist URL #1762

Merged
merged 6 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 150 additions & 98 deletions src/commands/game_options/spotify.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { IPCLogger } from "../../logger";
import { OptionAction, SPOTIFY_BASE_URL } from "../../constants";
import {
OptionAction,
SPOTIFY_BASE_URL,
SPOTIFY_SHORTHAND_BASE_URL,
} from "../../constants";
import {
friendlyFormattedNumber,
isValidURL,
Expand Down Expand Up @@ -147,6 +151,13 @@ export default class SpotifyCommand implements BaseCommand {
"command.spotify.help.example.playlistURL",
),
},
{
example: `\`/spotify playlist_url:${SPOTIFY_SHORTHAND_BASE_URL}...\``,
explanation: i18n.translate(
guildID,
"command.spotify.help.example.playlistURL",
),
},
{
example: "`/spotify`",
explanation: i18n.translate(
Expand Down Expand Up @@ -175,6 +186,12 @@ export default class SpotifyCommand implements BaseCommand {
playlistURL == null,
);
} else {
logger.warn(
`${getDebugLogHeader(
message,
)} | Invalid URL in call. playlistURL = ${playlistURL}`,
);

sendErrorMessage(MessageContext.fromMessage(message), {
title: i18n.translate(
message.guildID,
Expand Down Expand Up @@ -225,139 +242,174 @@ export default class SpotifyCommand implements BaseCommand {
return;
}

if (
isValidURL(playlistURL) &&
new RegExp(`^${SPOTIFY_BASE_URL}.+`).test(playlistURL)
) {
let playlistID = playlistURL.split(SPOTIFY_BASE_URL)[1];
if (playlistID.includes("?si=")) {
playlistID = playlistID.split("?si=")[0];
}
const isFullURL = new RegExp(`^${SPOTIFY_BASE_URL}.+`).test(
playlistURL,
);

const premiumRequest = await isPremiumRequest(
session,
messageContext.author.id,
);
const isShorthandURL = new RegExp(
`^${SPOTIFY_SHORTHAND_BASE_URL}.+`,
).test(playlistURL);

let matchedPlaylist: MatchedPlaylist;
if (session) {
matchedPlaylist = (await session.songSelector.reloadSongs(
guildPreference,
premiumRequest,
playlistID,
true,
messageContext,
interaction,
)) as MatchedPlaylist;
} else {
matchedPlaylist = (await new SongSelector().reloadSongs(
guildPreference,
premiumRequest,
playlistID,
true,
if (!isValidURL(playlistURL) || (!isFullURL && !isShorthandURL)) {
logger.warn(
`${getDebugLogHeader(
messageContext,
interaction,
)) as MatchedPlaylist;
}
)} | Invalid URL in updateOption. playlistURL = ${playlistURL}`,
);

logger.info(
`${getDebugLogHeader(messageContext)} | Matched ${
matchedPlaylist.metadata.matchedSongsLength
}/${matchedPlaylist.metadata.playlistLength} (${(
(100.0 * matchedPlaylist.metadata.matchedSongsLength) /
matchedPlaylist.metadata.playlistLength
).toFixed(2)}%) Spotify songs`,
sendErrorMessage(
messageContext,
{
title: i18n.translate(
messageContext.guildID,
"command.spotify.invalidURL.title",
),
description: i18n.translate(
messageContext.guildID,
"command.spotify.invalidURL.description",
),
},
interaction,
);
return;
}

let playlistID: string;
if (isFullURL) {
playlistID = playlistURL.split(SPOTIFY_BASE_URL)[1];
} else {
try {
const response = await fetch(playlistURL);
const body = await response.text();
playlistID = body.split(SPOTIFY_BASE_URL)[1].split("?si=")[0];
} catch (err) {
logger.error(
`${getDebugLogHeader(
messageContext,
)} | Failed to get playlist ID from shorthand Spotify URL. playlistURL = ${playlistURL}. err = ${err}`,
);

if (matchedPlaylist.matchedSongs.length === 0) {
sendErrorMessage(
messageContext,
{
title: i18n.translate(
guildID,
"command.spotify.noMatches.title",
messageContext.guildID,
"command.spotify.invalidURL.title",
),
description: i18n.translate(
guildID,
"command.spotify.noMatches.description",
messageContext.guildID,
"command.spotify.invalidURL.description",
),
},
interaction,
);

return;
}
}

await guildPreference.setSpotifyPlaylistID(playlistID);

await LimitCommand.updateOption(
messageContext,
0,
matchedPlaylist.metadata.matchedSongsLength,
undefined,
false,
);
if (playlistID.includes("?si=")) {
taahamahdi marked this conversation as resolved.
Show resolved Hide resolved
playlistID = playlistID.split("?si=")[0];
}

logger.info(
`${getDebugLogHeader(
messageContext,
)} | Spotify playlist set to ${playlistID}`,
);
const premiumRequest = await isPremiumRequest(
session,
messageContext.author.id,
);

let matchedDescription = i18n.translate(
guildID,
"command.spotify.matched.description",
{
matchedCount: friendlyFormattedNumber(
matchedPlaylist.matchedSongs.length,
),
totalCount: friendlyFormattedNumber(
matchedPlaylist.metadata.playlistLength,
),
},
);
let matchedPlaylist: MatchedPlaylist;
if (session) {
matchedPlaylist = (await session.songSelector.reloadSongs(
guildPreference,
premiumRequest,
playlistID,
true,
messageContext,
interaction,
)) as MatchedPlaylist;
} else {
matchedPlaylist = (await new SongSelector().reloadSongs(
guildPreference,
premiumRequest,
playlistID,
true,
messageContext,
interaction,
)) as MatchedPlaylist;
}

if (matchedPlaylist.truncated) {
matchedDescription += "\n\n";
matchedDescription += italicize(
i18n.translate(
guildID,
"command.spotify.matched.truncated",
),
);
}
logger.info(
`${getDebugLogHeader(messageContext)} | Matched ${
matchedPlaylist.metadata.matchedSongsLength
}/${matchedPlaylist.metadata.playlistLength} (${(
(100.0 * matchedPlaylist.metadata.matchedSongsLength) /
matchedPlaylist.metadata.playlistLength
).toFixed(2)}%) Spotify songs`,
);

await sendInfoMessage(messageContext, {
title: i18n.translate(
guildID,
"command.spotify.matched.title",
{
playlistName: matchedPlaylist.metadata.playlistName,
},
),
description: matchedDescription,
url: playlistURL,
thumbnailUrl:
matchedPlaylist.metadata.thumbnailUrl ?? undefined,
});
} else {
if (matchedPlaylist.matchedSongs.length === 0) {
sendErrorMessage(
messageContext,
{
title: i18n.translate(
messageContext.guildID,
"command.spotify.invalidURL.title",
guildID,
"command.spotify.noMatches.title",
),
description: i18n.translate(
messageContext.guildID,
"command.spotify.invalidURL.description",
guildID,
"command.spotify.noMatches.description",
),
},
interaction,
);

return;
}

await guildPreference.setSpotifyPlaylistID(playlistID);

await LimitCommand.updateOption(
messageContext,
0,
matchedPlaylist.metadata.matchedSongsLength,
undefined,
false,
);

logger.info(
`${getDebugLogHeader(
messageContext,
)} | Spotify playlist set to ${playlistID}`,
);

let matchedDescription = i18n.translate(
guildID,
"command.spotify.matched.description",
{
matchedCount: friendlyFormattedNumber(
matchedPlaylist.matchedSongs.length,
),
totalCount: friendlyFormattedNumber(
matchedPlaylist.metadata.playlistLength,
),
},
);

if (matchedPlaylist.truncated) {
matchedDescription += "\n\n";
matchedDescription += italicize(
i18n.translate(guildID, "command.spotify.matched.truncated"),
);
}

await sendInfoMessage(messageContext, {
title: i18n.translate(guildID, "command.spotify.matched.title", {
playlistName: matchedPlaylist.metadata.playlistName,
}),
description: matchedDescription,
url: playlistURL,
thumbnailUrl: matchedPlaylist.metadata.thumbnailUrl ?? undefined,
});

if (interaction?.acknowledged) {
const optionsEmbed = await generateOptionsMessage(
session,
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export const STATUS_COOKIE = path.join(__dirname, "../status");

export const PERMISSIONS_LINK = "https://www.youtube.com/watch?v=87GW0SmF5LI";
export const SPOTIFY_BASE_URL = "https://open.spotify.com/playlist/";
export const SPOTIFY_SHORTHAND_BASE_URL = "https://spotify.link/";

export enum GroupAction {
ADD = "add",
Expand Down