Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7a5db37
feat: initial commit! yay
nexpid Feb 2, 2026
693db92
feat(csp): add SongSpotlight & audio previews
nexpid Feb 2, 2026
3b40d57
feat(utils/clipboard): add readClipboard
nexpid Feb 2, 2026
f5e8c2b
chore: inshtall @song-spotlight/api
nexpid Feb 2, 2026
59c10f5
chore: and oh yeah add myself to contribs
nexpid Feb 2, 2026
41ed1dc
Merge branch 'dev' into plogon/song-spotlight
nexpid Feb 2, 2026
a4308cc
feat: ditch data uris for CSP whitelist
nexpid Feb 2, 2026
6dee06b
style: fix up some css again
nexpid Feb 3, 2026
0a940be
chore: bump @song-spotlight/api to 2.0.0
nexpid Feb 4, 2026
b397645
feat: add player circular progress
nexpid Feb 4, 2026
5c09fa2
chore: use parser.label
nexpid Feb 4, 2026
b1a7fb8
fix: fix oauth2
nexpid Feb 4, 2026
9103329
feat: handle Unauthorized
nexpid Feb 4, 2026
69c71e2
fix: update patch for latest canary
nexpid Feb 4, 2026
d15cb68
fix: update second patch for latest canary
nexpid Feb 4, 2026
1eee0df
feat: update icons
nexpid Feb 5, 2026
ee567e9
chore: update some code
nexpid Feb 5, 2026
52688d9
fix: fix MoreHorizontalIcon size prop
nexpid Feb 5, 2026
b721bc7
Merge branch 'dev' into plogon/song-spotlight
nexpid Feb 5, 2026
4cba5e6
Merge branch 'dev' into plogon/song-spotlight
nexpid Feb 6, 2026
ad53283
feat: add rearranging & optimize css(?)
nexpid Feb 6, 2026
9e7eeeb
fix: oh right it should autoplay
nexpid Feb 6, 2026
3387934
feat: some code reorganizations
nexpid Mar 1, 2026
e6c95a5
Merge branch 'dev' into plogon/song-spotlight
nexpid Mar 1, 2026
27478e3
feat: more code reorganization
nexpid Mar 1, 2026
79be483
fix(SongSpotlight): ditch React.memo & import updates
nexpid Mar 2, 2026
3839e8a
fix(SongSpotlight): Run autofix to sort these imports!
nexpid Mar 2, 2026
2183731
feat(SongSpotlight): backport from Equi
nexpid Mar 3, 2026
ee351d2
feat(SongSpotlight): support refresh tokens
nexpid Mar 4, 2026
349232f
chore(SongSpotlight): unstub
nexpid Mar 5, 2026
79a5cfd
Merge branch 'dev' into plogon/song-spotlight
nexpid Mar 21, 2026
274b47c
SongSpotlight: add tidal & UI updates
nexpid Mar 21, 2026
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
},
"dependencies": {
"@intrnl/xxhash64": "^0.1.2",
"@song-spotlight/api": "^2.1.3",
"@vap/core": "0.0.12",
"@vap/shiki": "0.10.5",
"fflate": "^0.8.2",
Expand Down
108 changes: 105 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/main/csp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type PolicyMap = Record<string, string[]>;

export const ConnectSrc = ["connect-src"];
export const ImageSrc = [...ConnectSrc, "img-src"];
export const MediaSrc = [...ImageSrc, "media-src"];
export const CssSrc = ["style-src", "font-src"];
export const ImageAndCssSrc = [...ImageSrc, ...CssSrc];
export const ImageScriptsAndCssSrc = [...ImageAndCssSrc, "script-src", "worker-src"];
Expand Down Expand Up @@ -63,6 +64,12 @@ export const CspPolicies: PolicyMap = {
"dearrow-thumb.ajay.app": ImageSrc, // Dearrow Thumbnail CDN
"usrbg.is-hardly.online": ImageSrc, // USRBG API
"icons.duckduckgo.com": ImageSrc, // DuckDuckGo Favicon API (Reverse Image Search)
"dc.songspotlight.nexpid.xyz": ConnectSrc, // Song Spotlight API
"*.scdn.co": MediaSrc, // Spotify cover art/audio preview CDN
"*.sndcdn.com": MediaSrc, // Soundcloud cover art/audio preview CDN
"*.mzstatic.com": ImageSrc, // Apple Music cover art CDN
"audio-ssl.itunes.apple.com": MediaSrc, // Apple Music audio preview CDN
"*.tidal.com": MediaSrc, // Tidal cover art/audio preview CDN
};

const findHeader = (headers: PolicyMap, headerName: Lowercase<string>) => {
Expand Down
85 changes: 85 additions & 0 deletions src/plugins/songSpotlight.desktop/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2026 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/

import "./style.css";

import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";

import { useAuthorizationStore } from "./lib/stores/AuthorizationStore";
import { useSongStore } from "./lib/stores/SongStore";
import { Native } from "./service";
import settings from "./settings";
import ProfileSongs from "./ui/songs/ProfileSongs";
import WidgetSongs from "./ui/songs/WidgetSongs";

export default definePlugin({
name: "SongSpotlight",
description: "Show off songs on your profile",
authors: [Devs.nexpid],
settings,
patches: [
// Personal profile popout
{
find: '"UserProfileAccountPopout"',
replacement: {
match: /user:(\i),bio:.{0,60}}\)/,
replace: "$&,$self.renderProfileSongs({user:$1})",
},
},
// Message user popout
{
find: ".isProvisional?(",
replacement: {
match: /user:(\i),bio:.{0,60}}\)/,
replace: "$&,$self.renderProfileSongs({user:$1})",
},
},
// DM sidebar profile
{
find: ".SIDEBAR}),nicknameIcons:",
replacement: {
match: /{userId:(\i)\.id}\)}\).{0,100}]}\)(?=\]\}\))/,
replace: "$&,$self.renderProfileSongs({user:$1,isSideBar:true})",
},
},
// Full profile modal sections (lazy loaded)
{
find: ".MUTUAL_GUILDS})),",
replacement: {
match: /(\i).push\({text.{0,50}}\);/,
replace: '$&$1.push({text:"Song Spotlight",section:"SONG_SPOTLIGHT"});',
},
},
{
find: ".hasUnsavedChanges()&&",
replacement: {
match: /({user:(\i),.{0,80}return (\i===))/,
replace: '$1"SONG_SPOTLIGHT"?$self.renderWidgetSongs({user:$2}):$3',
},
},
],

flux: {
CONNECTION_OPEN: () => {
useSongStore.getState().$refresh();
},
},
start() {
// the cache lives in native.ts so it persists between reloads and
// only gets cleared on full restart. we don't want that since
// audio preview URLs expire very fast, so we just clear it on
// plugin restart instead
Native.clearCache();

useSongStore.getState().$refresh();
useAuthorizationStore.persist.rehydrate();
},

renderProfileSongs: ErrorBoundary.wrap(ProfileSongs, { noop: true }),
renderWidgetSongs: ErrorBoundary.wrap(WidgetSongs, { noop: true }),
});
Loading
Loading