feat(plugins/openInApp) Refactor code and add Apple Music support (#2744)
Co-authored-by: v <vendicated@riseup.net> Co-authored-by: Shiggy <136832773+shiggybot@users.noreply.github.com>
This commit is contained in:
parent
0f5cf37ef9
commit
51ae019cd5
4 changed files with 84 additions and 82 deletions
|
@ -35,7 +35,8 @@ export const ALLOWED_PROTOCOLS = [
|
||||||
"steam:",
|
"steam:",
|
||||||
"spotify:",
|
"spotify:",
|
||||||
"com.epicgames.launcher:",
|
"com.epicgames.launcher:",
|
||||||
"tidal:"
|
"tidal:",
|
||||||
|
"itunes:",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const IS_VANILLA = /* @__PURE__ */ process.argv.includes("--vanilla");
|
export const IS_VANILLA = /* @__PURE__ */ process.argv.includes("--vanilla");
|
||||||
|
|
11
src/plugins/openInApp/README.md
Normal file
11
src/plugins/openInApp/README.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# OpenInApp
|
||||||
|
|
||||||
|
Open links in their respective apps instead of your browser
|
||||||
|
|
||||||
|
## Currently supports:
|
||||||
|
|
||||||
|
- Spotify
|
||||||
|
- Steam
|
||||||
|
- EpicGames
|
||||||
|
- Tidal
|
||||||
|
- Apple Music (iTunes)
|
|
@ -18,46 +18,70 @@
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType, PluginNative } from "@utils/types";
|
import definePlugin, { OptionType, PluginNative, SettingsDefinition } from "@utils/types";
|
||||||
import { showToast, Toasts } from "@webpack/common";
|
import { showToast, Toasts } from "@webpack/common";
|
||||||
import type { MouseEvent } from "react";
|
import type { MouseEvent } from "react";
|
||||||
|
|
||||||
const ShortUrlMatcher = /^https:\/\/(spotify\.link|s\.team)\/.+$/;
|
interface URLReplacementRule {
|
||||||
const SpotifyMatcher = /^https:\/\/open\.spotify\.com\/(track|album|artist|playlist|user|episode)\/(.+)(?:\?.+?)?$/;
|
match: RegExp;
|
||||||
const SteamMatcher = /^https:\/\/(steamcommunity\.com|(?:help|store)\.steampowered\.com)\/.+$/;
|
replace: (...matches: string[]) => string;
|
||||||
const EpicMatcher = /^https:\/\/store\.epicgames\.com\/(.+)$/;
|
description: string;
|
||||||
const TidalMatcher = /^https:\/\/tidal\.com\/browse\/(track|album|artist|playlist|user|video|mix)\/(.+)(?:\?.+?)?$/;
|
shortlinkMatch?: RegExp;
|
||||||
|
accountViewReplace?: (userId: string) => string;
|
||||||
|
}
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
// Do not forget to add protocols to the ALLOWED_PROTOCOLS constant
|
||||||
|
const UrlReplacementRules: Record<string, URLReplacementRule> = {
|
||||||
spotify: {
|
spotify: {
|
||||||
type: OptionType.BOOLEAN,
|
match: /^https:\/\/open\.spotify\.com\/(track|album|artist|playlist|user|episode)\/(.+)(?:\?.+?)?$/,
|
||||||
|
replace: (_, type, id) => `spotify://${type}/${id}`,
|
||||||
description: "Open Spotify links in the Spotify app",
|
description: "Open Spotify links in the Spotify app",
|
||||||
default: true,
|
shortlinkMatch: /^https:\/\/spotify\.link\/.+$/,
|
||||||
|
accountViewReplace: userId => `spotify:user:${userId}`,
|
||||||
},
|
},
|
||||||
steam: {
|
steam: {
|
||||||
type: OptionType.BOOLEAN,
|
match: /^https:\/\/(steamcommunity\.com|(?:help|store)\.steampowered\.com)\/.+$/,
|
||||||
|
replace: match => `steam://openurl/${match}`,
|
||||||
description: "Open Steam links in the Steam app",
|
description: "Open Steam links in the Steam app",
|
||||||
default: true,
|
shortlinkMatch: /^https:\/\/s.team\/.+$/,
|
||||||
|
accountViewReplace: userId => `steam://openurl/https://steamcommunity.com/profiles/${userId}`,
|
||||||
},
|
},
|
||||||
epic: {
|
epic: {
|
||||||
type: OptionType.BOOLEAN,
|
match: /^https:\/\/store\.epicgames\.com\/(.+)$/,
|
||||||
|
replace: (_, id) => `com.epicgames.launcher://store/${id}`,
|
||||||
description: "Open Epic Games links in the Epic Games Launcher",
|
description: "Open Epic Games links in the Epic Games Launcher",
|
||||||
default: true,
|
|
||||||
},
|
},
|
||||||
tidal: {
|
tidal: {
|
||||||
type: OptionType.BOOLEAN,
|
match: /^https:\/\/tidal\.com\/browse\/(track|album|artist|playlist|user|video|mix)\/(.+)(?:\?.+?)?$/,
|
||||||
|
replace: (_, type, id) => `tidal://${type}/${id}`,
|
||||||
description: "Open Tidal links in the Tidal app",
|
description: "Open Tidal links in the Tidal app",
|
||||||
|
},
|
||||||
|
itunes: {
|
||||||
|
match: /^https:\/\/music\.apple\.com\/([a-z]{2}\/)?(album|artist|playlist|song|curator)\/([^/?#]+)\/?([^/?#]+)?(?:\?.*)?(?:#.*)?$/,
|
||||||
|
replace: (_, lang, type, name, id) => id ? `itunes://music.apple.com/us/${type}/${name}/${id}` : `itunes://music.apple.com/us/${type}/${name}`,
|
||||||
|
description: "Open Apple Music links in the iTunes app"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const pluginSettings = definePluginSettings(
|
||||||
|
Object.entries(UrlReplacementRules).reduce((acc, [key, rule]) => {
|
||||||
|
acc[key] = {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: rule.description,
|
||||||
default: true,
|
default: true,
|
||||||
}
|
};
|
||||||
});
|
return acc;
|
||||||
|
}, {} as SettingsDefinition)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
const Native = VencordNative.pluginHelpers.OpenInApp as PluginNative<typeof import("./native")>;
|
const Native = VencordNative.pluginHelpers.OpenInApp as PluginNative<typeof import("./native")>;
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "OpenInApp",
|
name: "OpenInApp",
|
||||||
description: "Open Spotify, Tidal, Steam and Epic Games URLs in their respective apps instead of your browser",
|
description: "Open links in their respective apps instead of your browser",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven, Devs.surgedevs],
|
||||||
settings,
|
settings: pluginSettings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
|
@ -70,7 +94,7 @@ export default definePlugin({
|
||||||
// Make Spotify profile activity links open in app on web
|
// Make Spotify profile activity links open in app on web
|
||||||
{
|
{
|
||||||
find: "WEB_OPEN(",
|
find: "WEB_OPEN(",
|
||||||
predicate: () => !IS_DISCORD_DESKTOP && settings.store.spotify,
|
predicate: () => !IS_DISCORD_DESKTOP && pluginSettings.store.spotify,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\i\.\i\.isProtocolRegistered\(\)(.{0,100})window.open/g,
|
match: /\i\.\i\.isProtocolRegistered\(\)(.{0,100})window.open/g,
|
||||||
replace: "true$1VencordNative.native.openExternal"
|
replace: "true$1VencordNative.native.openExternal"
|
||||||
|
@ -79,8 +103,8 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ".CONNECTED_ACCOUNT_VIEWED,",
|
find: ".CONNECTED_ACCOUNT_VIEWED,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=href:\i,onClick:\i=>\{)(?=.{0,10}\i=(\i)\.type,.{0,100}CONNECTED_ACCOUNT_VIEWED)/,
|
match: /(?<=href:\i,onClick:(\i)=>\{)(?=.{0,10}\i=(\i)\.type,.{0,100}CONNECTED_ACCOUNT_VIEWED)/,
|
||||||
replace: "$self.handleAccountView(arguments[0],$1.type,$1.id);"
|
replace: "if($self.handleAccountView($1,$2.type,$2.id)) return;"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -89,61 +113,25 @@ export default definePlugin({
|
||||||
if (!data) return false;
|
if (!data) return false;
|
||||||
|
|
||||||
let url = data.href;
|
let url = data.href;
|
||||||
if (!IS_WEB && ShortUrlMatcher.test(url)) {
|
if (!url) return false;
|
||||||
|
|
||||||
|
for (const [key, rule] of Object.entries(UrlReplacementRules)) {
|
||||||
|
if (!pluginSettings.store[key]) continue;
|
||||||
|
|
||||||
|
if (rule.shortlinkMatch?.test(url)) {
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
// CORS jumpscare
|
|
||||||
url = await Native.resolveRedirect(url);
|
url = await Native.resolveRedirect(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
spotify: {
|
if (rule.match.test(url)) {
|
||||||
if (!settings.store.spotify) break spotify;
|
showToast("Opened link in native app", Toasts.Type.SUCCESS);
|
||||||
|
|
||||||
const match = SpotifyMatcher.exec(url);
|
const newUrl = url.replace(rule.match, rule.replace);
|
||||||
if (!match) break spotify;
|
VencordNative.native.openExternal(newUrl);
|
||||||
|
|
||||||
const [, type, id] = match;
|
|
||||||
VencordNative.native.openExternal(`spotify:${type}:${id}`);
|
|
||||||
|
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
steam: {
|
|
||||||
if (!settings.store.steam) break steam;
|
|
||||||
|
|
||||||
if (!SteamMatcher.test(url)) break steam;
|
|
||||||
|
|
||||||
VencordNative.native.openExternal(`steam://openurl/${url}`);
|
|
||||||
event?.preventDefault();
|
|
||||||
|
|
||||||
// Steam does not focus itself so show a toast so it's slightly less confusing
|
|
||||||
showToast("Opened link in Steam", Toasts.Type.SUCCESS);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
epic: {
|
|
||||||
if (!settings.store.epic) break epic;
|
|
||||||
|
|
||||||
const match = EpicMatcher.exec(url);
|
|
||||||
if (!match) break epic;
|
|
||||||
|
|
||||||
VencordNative.native.openExternal(`com.epicgames.launcher://store/${match[1]}`);
|
|
||||||
event?.preventDefault();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
tidal: {
|
|
||||||
if (!settings.store.tidal) break tidal;
|
|
||||||
|
|
||||||
const match = TidalMatcher.exec(url);
|
|
||||||
if (!match) break tidal;
|
|
||||||
|
|
||||||
const [, type, id] = match;
|
|
||||||
VencordNative.native.openExternal(`tidal://${type}/${id}`);
|
|
||||||
|
|
||||||
event?.preventDefault();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// in case short url didn't end up being something we can handle
|
// in case short url didn't end up being something we can handle
|
||||||
|
@ -155,14 +143,12 @@ export default definePlugin({
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleAccountView(event: { preventDefault(): void; }, platformType: string, userId: string) {
|
handleAccountView(e: MouseEvent, platformType: string, userId: string) {
|
||||||
if (platformType === "spotify" && settings.store.spotify) {
|
const rule = UrlReplacementRules[platformType];
|
||||||
VencordNative.native.openExternal(`spotify:user:${userId}`);
|
if (rule?.accountViewReplace && pluginSettings.store[platformType]) {
|
||||||
event.preventDefault();
|
VencordNative.native.openExternal(rule.accountViewReplace(userId));
|
||||||
} else if (platformType === "steam" && settings.store.steam) {
|
e.preventDefault();
|
||||||
VencordNative.native.openExternal(`steam://openurl/https://steamcommunity.com/profiles/${userId}`);
|
return true;
|
||||||
showToast("Opened link in Steam", Toasts.Type.SUCCESS);
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -538,6 +538,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "Joona",
|
name: "Joona",
|
||||||
id: 297410829589020673n
|
id: 297410829589020673n
|
||||||
},
|
},
|
||||||
|
surgedevs: {
|
||||||
|
name: "Chloe",
|
||||||
|
id: 1084592643784331324n
|
||||||
|
}
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
// iife so #__PURE__ works correctly
|
// iife so #__PURE__ works correctly
|
||||||
|
|
Loading…
Reference in a new issue