mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-18 02:47:03 -04:00
SpotifyControls plugin (#190)
This commit is contained in:
parent
7d5ade21fc
commit
6a8564089b
9 changed files with 852 additions and 64 deletions
203
src/plugins/spotifyControls/SpotifyStore.ts
Normal file
203
src/plugins/spotifyControls/SpotifyStore.ts
Normal file
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import cssText from "~fileContent/styles.css";
|
||||
|
||||
import { IpcEvents, lazyWebpack, proxyLazy } from "../../utils";
|
||||
import { filters } from "../../webpack";
|
||||
import { Flux, FluxDispatcher } from "../../webpack/common";
|
||||
|
||||
export interface Track {
|
||||
id: string;
|
||||
name: string;
|
||||
duration: number;
|
||||
isLocal: boolean;
|
||||
album: {
|
||||
id: string;
|
||||
name: string;
|
||||
image: {
|
||||
height: number;
|
||||
width: number;
|
||||
url: string;
|
||||
};
|
||||
};
|
||||
artists: {
|
||||
id: string;
|
||||
href: string;
|
||||
name: string;
|
||||
type: string;
|
||||
uri: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
interface PlayerState {
|
||||
accountId: string;
|
||||
track: Track | null;
|
||||
volumePercent: number,
|
||||
isPlaying: boolean,
|
||||
repeat: boolean,
|
||||
position: number,
|
||||
context?: any;
|
||||
device?: Device;
|
||||
|
||||
// added by patch
|
||||
actual_repeat: Repeat;
|
||||
}
|
||||
|
||||
interface Device {
|
||||
id: string;
|
||||
is_active: boolean;
|
||||
}
|
||||
|
||||
type Repeat = "off" | "track" | "context";
|
||||
|
||||
// Don't wanna run before Flux and Dispatcher are ready!
|
||||
export const SpotifyStore = proxyLazy(() => {
|
||||
// TODO: Move this elsewhere
|
||||
const style = document.createElement("style");
|
||||
style.innerText = cssText;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// For some reason ts hates extends Flux.Store
|
||||
const { Store } = Flux;
|
||||
|
||||
const SpotifySocket = lazyWebpack(filters.byProps("getActiveSocketAndDevice"));
|
||||
const SpotifyAPI = lazyWebpack(filters.byProps("SpotifyAPIMarker"));
|
||||
|
||||
const API_BASE = "https://api.spotify.com/v1/me/player";
|
||||
|
||||
class SpotifyStore extends Store {
|
||||
constructor(dispatcher: any, handlers: any) {
|
||||
super(dispatcher, handlers);
|
||||
}
|
||||
|
||||
public mPosition = 0;
|
||||
private start = 0;
|
||||
|
||||
public track: Track | null = null;
|
||||
public device: Device | null = null;
|
||||
public isPlaying = false;
|
||||
public repeat: Repeat = "off";
|
||||
public shuffle = false;
|
||||
public volume = 0;
|
||||
|
||||
public isSettingPosition = false;
|
||||
|
||||
public openExternal(path: string) {
|
||||
VencordNative.ipc.invoke(IpcEvents.OPEN_EXTERNAL, "https://open.spotify.com" + path);
|
||||
}
|
||||
|
||||
// Need to keep track of this manually
|
||||
public get position(): number {
|
||||
let pos = this.mPosition;
|
||||
if (this.isPlaying) {
|
||||
pos += Date.now() - this.start;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
public set position(p: number) {
|
||||
this.mPosition = p;
|
||||
this.start = Date.now();
|
||||
}
|
||||
|
||||
prev() {
|
||||
this.req("post", "/previous");
|
||||
}
|
||||
|
||||
next() {
|
||||
this.req("post", "/next");
|
||||
}
|
||||
|
||||
setVolume(percent: number) {
|
||||
this.req("put", "/volume", {
|
||||
query: {
|
||||
volume_percent: Math.round(percent)
|
||||
}
|
||||
|
||||
}).then(() => {
|
||||
this.volume = percent;
|
||||
this.emitChange();
|
||||
});
|
||||
}
|
||||
|
||||
setPlaying(playing: boolean) {
|
||||
this.req("put", playing ? "/play" : "/pause");
|
||||
}
|
||||
|
||||
setRepeat(state: Repeat) {
|
||||
this.req("put", "/repeat", {
|
||||
query: { state }
|
||||
});
|
||||
}
|
||||
|
||||
setShuffle(state: boolean) {
|
||||
this.req("put", "/shuffle", {
|
||||
query: { state }
|
||||
}).then(() => {
|
||||
this.shuffle = state;
|
||||
this.emitChange();
|
||||
});
|
||||
}
|
||||
|
||||
seek(ms: number) {
|
||||
if (this.isSettingPosition) return Promise.resolve();
|
||||
|
||||
this.isSettingPosition = true;
|
||||
|
||||
return this.req("put", "/seek", {
|
||||
query: {
|
||||
position_ms: Math.round(ms)
|
||||
}
|
||||
}).catch((e: any) => {
|
||||
console.error("[VencordSpotifyControls] Failed to seek", e);
|
||||
this.isSettingPosition = false;
|
||||
});
|
||||
}
|
||||
|
||||
private req(method: "post" | "get" | "put", route: string, data: any = {}) {
|
||||
if (this.device?.is_active)
|
||||
(data.query ??= {}).device_id = this.device.id;
|
||||
|
||||
const { socket } = SpotifySocket.getActiveSocketAndDevice();
|
||||
return SpotifyAPI[method](socket.accountId, socket.accessToken, {
|
||||
url: API_BASE + route,
|
||||
...data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const store = new SpotifyStore(FluxDispatcher, {
|
||||
SPOTIFY_PLAYER_STATE(e: PlayerState) {
|
||||
store.track = e.track;
|
||||
store.device = e.device ?? null;
|
||||
store.isPlaying = e.isPlaying ?? false;
|
||||
store.volume = e.volumePercent ?? 0;
|
||||
store.repeat = e.actual_repeat || "off";
|
||||
store.position = e.position ?? 0;
|
||||
store.isSettingPosition = false;
|
||||
store.emitChange();
|
||||
},
|
||||
SPOTIFY_SET_DEVICES({ devices }: { devices: Device[]; }) {
|
||||
store.device = devices.find(d => d.is_active) ?? devices[0] ?? null;
|
||||
store.emitChange();
|
||||
}
|
||||
});
|
||||
|
||||
return store;
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue