Move YTM To WS Only
This commit is contained in:
parent
d06fcf84e0
commit
ca7c8c02d5
5 changed files with 65 additions and 122 deletions
|
|
@ -109,16 +109,16 @@ function makeContextMenu(name: string, path: string) {
|
|||
}
|
||||
|
||||
function Controls() {
|
||||
const [isPlaying, repeat] = useStateFromStores<[boolean, Repeat]>(
|
||||
const [isPlaying, shuffle, repeat] = useStateFromStores(
|
||||
[YoutubeMusicStore],
|
||||
() => [YoutubeMusicStore.isPlaying, YoutubeMusicStore.repeat]
|
||||
() => [YoutubeMusicStore.isPlaying, YoutubeMusicStore.shuffle, YoutubeMusicStore.repeat]
|
||||
);
|
||||
|
||||
const [nextRepeat, repeatClassName] = (() => {
|
||||
switch (repeat) {
|
||||
case "NONE": return ["context", "repeat-off"] as const;
|
||||
case "ALL": return ["track", "repeat-context"] as const;
|
||||
case "ONE": return ["off", "repeat-track"] as const;
|
||||
case "NONE": return ["ALL", "repeat-off"] as const;
|
||||
case "ALL": return ["ONE", "repeat-context"] as const;
|
||||
case "ONE": return ["NONE", "repeat-track"] as const;
|
||||
default: throw new Error(`Invalid repeat state ${repeat}`);
|
||||
}
|
||||
})();
|
||||
|
|
@ -128,11 +128,11 @@ function Controls() {
|
|||
<Flex className={cl("button-row")} style={{ gap: 0 }}>
|
||||
<Button
|
||||
className={classes(cl("button"), cl("shuffle-off"))}
|
||||
onClick={() => YoutubeMusicStore.shuffle()}
|
||||
onClick={() => YoutubeMusicStore.setShuffle(!shuffle)}
|
||||
>
|
||||
<Shuffle />
|
||||
</Button>
|
||||
<Button onClick={() => YoutubeMusicStore.prev()}>
|
||||
<Button onClick={() => YoutubeMusicStore.previous()}>
|
||||
<SkipPrev />
|
||||
</Button>
|
||||
<Button onClick={() => YoutubeMusicStore.setPlaying(!isPlaying)}>
|
||||
|
|
@ -143,8 +143,7 @@ function Controls() {
|
|||
</Button>
|
||||
<Button
|
||||
className={classes(cl("button"), cl(repeatClassName))}
|
||||
onClick={() => YoutubeMusicStore.switchRepeat()}
|
||||
style={{ position: "relative" }}
|
||||
onClick={() => YoutubeMusicStore.setRepeat(nextRepeat)}
|
||||
>
|
||||
{repeat === "ONE" && <span className={cl("repeat-1")}>1</span>}
|
||||
<Repeat />
|
||||
|
|
@ -158,28 +157,30 @@ const seek = debounce((v: number) => {
|
|||
});
|
||||
|
||||
function YtmSeekBar() {
|
||||
const { songDuration } = YoutubeMusicStore.song!;
|
||||
const { songDuration, videoId } = YoutubeMusicStore.song ?? {};
|
||||
|
||||
const [storePosition, isSettingPosition, isPlaying] = useStateFromStores(
|
||||
const [storePosition, isPlaying] = useStateFromStores(
|
||||
[YoutubeMusicStore],
|
||||
() => [YoutubeMusicStore.mPosition, YoutubeMusicStore.isSettingPosition, YoutubeMusicStore.isPlaying]
|
||||
() => [YoutubeMusicStore.mPosition, YoutubeMusicStore.isPlaying]
|
||||
);
|
||||
|
||||
const [position, setPosition] = useState<number>(storePosition);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying && !isSettingPosition) {
|
||||
setPosition(storePosition);
|
||||
}, [videoId, songDuration, storePosition]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
setPosition(YoutubeMusicStore.position);
|
||||
const interval = setInterval(() => {
|
||||
setPosition(p => p + 1000);
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [storePosition, isSettingPosition, isPlaying]);
|
||||
}, [storePosition, isPlaying]);
|
||||
|
||||
const onChange = (v: number) => {
|
||||
if (isSettingPosition) return;
|
||||
setPosition(v);
|
||||
seek(v);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -55,25 +55,13 @@ export interface PlayerState {
|
|||
|
||||
export type Repeat = "NONE" | "ONE" | "ALL";
|
||||
|
||||
const nextRepeat = (repeat: Repeat) => {
|
||||
switch (repeat) {
|
||||
case "NONE":
|
||||
return "ALL" as const;
|
||||
case "ALL":
|
||||
return "ONE" as const;
|
||||
case "ONE":
|
||||
return "NONE" as const;
|
||||
}
|
||||
};
|
||||
|
||||
const logger = new Logger("YoutubeMusicControls");
|
||||
|
||||
type Message = { type: "PLAYER_STATE"; } & PlayerState;
|
||||
|
||||
class YoutubemusicSocket {
|
||||
private PORT = 26539;
|
||||
class YoutubeMusicSocket {
|
||||
public onChange: (e: Message) => void;
|
||||
private ready = false;
|
||||
public ready = false;
|
||||
|
||||
private socket: WebSocket | undefined;
|
||||
|
||||
|
|
@ -97,7 +85,7 @@ class YoutubemusicSocket {
|
|||
return {
|
||||
"play": () => this.socket?.send(JSON.stringify({ type: "ACTION", action: "play" })),
|
||||
"pause": () => this.socket?.send(JSON.stringify({ type: "ACTION", action: "pause" })),
|
||||
"prev": () => this.socket?.send(JSON.stringify({ type: "ACTION", action: "prev" })),
|
||||
"previous": () => this.socket?.send(JSON.stringify({ type: "ACTION", action: "previous" })),
|
||||
"next": () => this.socket?.send(JSON.stringify({ type: "ACTION", action: "next" })),
|
||||
"seek": (seconds: number) => this.socket?.send(JSON.stringify({ type: "ACTION", action: "seek", data: seconds })),
|
||||
"shuffle": () => this.socket?.send(JSON.stringify({ type: "ACTION", action: "shuffle" })),
|
||||
|
|
@ -115,7 +103,6 @@ class YoutubemusicSocket {
|
|||
this.socket = new WebSocket(url);
|
||||
|
||||
this.socket.addEventListener("open", () => {
|
||||
// logger.info("Connected to YouTube Music WebSocket");
|
||||
this.ready = true;
|
||||
this.routes.pause();
|
||||
this.routes.play();
|
||||
|
|
@ -129,10 +116,8 @@ class YoutubemusicSocket {
|
|||
});
|
||||
|
||||
this.socket.addEventListener("close", e => {
|
||||
// logger.info("YouTube Music Socket Disconnected:", e.code, e.reason);
|
||||
this.ready = false;
|
||||
if (!this.ready) setTimeout(() => this.reconnect(), 10_000);
|
||||
// this.tryConnect();
|
||||
|
||||
this.onChange({ type: "PLAYER_STATE", song: null, isPlaying: false, position: 0, repeat: "NONE", volume: 0 });
|
||||
});
|
||||
|
|
@ -167,11 +152,10 @@ export const YoutubeMusicStore = proxyLazyWebpack(() => {
|
|||
public song: Song | null = null;
|
||||
public isPlaying = false;
|
||||
public repeat: Repeat = "NONE";
|
||||
public shuffle = false;
|
||||
public volume = 0;
|
||||
|
||||
public isSettingPosition = false;
|
||||
|
||||
public socket = new YoutubemusicSocket((message: Message) => {
|
||||
public socket = new YoutubeMusicSocket((message: Message) => {
|
||||
if (message.song) {
|
||||
store.song = message.song;
|
||||
store.position = message.song.elapsedSeconds ?? 0;
|
||||
|
|
@ -181,7 +165,6 @@ export const YoutubeMusicStore = proxyLazyWebpack(() => {
|
|||
if (message.volume) store.volume = message.volume ?? 0;
|
||||
if (message.repeat) store.repeat = message.repeat;
|
||||
|
||||
store.isSettingPosition = false;
|
||||
store.emitChange();
|
||||
});
|
||||
|
||||
|
|
@ -192,13 +175,9 @@ export const YoutubeMusicStore = proxyLazyWebpack(() => {
|
|||
? encodeURI("youtubemusic://openVideo " + videoId[1])
|
||||
: "https://music.youtube.com" + path;
|
||||
|
||||
console.log("Open", url);
|
||||
|
||||
// https://music.youtube.com/watch?v=BSHYPb15W-Y
|
||||
VencordNative.native.openExternal(url);
|
||||
}
|
||||
|
||||
|
||||
set position(p: number) {
|
||||
this.mPosition = p * 1000;
|
||||
this.start = Date.now();
|
||||
|
|
@ -212,82 +191,51 @@ export const YoutubeMusicStore = proxyLazyWebpack(() => {
|
|||
return pos;
|
||||
}
|
||||
|
||||
prev() {
|
||||
// this.socket.routes.prev();
|
||||
this.req("post", "/api/v1/previous");
|
||||
previous() {
|
||||
if (!this.ensureSocketReady()) return;
|
||||
this.socket.routes.previous();
|
||||
}
|
||||
next() {
|
||||
// this.socket.routes.next();
|
||||
this.req("post", "/api/v1/next");
|
||||
if (!this.ensureSocketReady()) return;
|
||||
this.socket.routes.next();
|
||||
}
|
||||
setVolume(percent: number) {
|
||||
// this.socket.routes.setVolume(percent);
|
||||
console.log(Math.floor(percent));
|
||||
this.req("post", "/api/v1/volume", {
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: {
|
||||
volume: Math.floor(percent)
|
||||
},
|
||||
});
|
||||
if (!this.ensureSocketReady()) return;
|
||||
this.socket.routes.setVolume(Math.round(percent));
|
||||
this.volume = percent;
|
||||
this.emitChange();
|
||||
}
|
||||
setPlaying(playing: boolean) {
|
||||
if (playing) {
|
||||
// YoutubemusicAPI.routes.pause.actions.do();
|
||||
this.req("post", "/api/v1/play");
|
||||
// this.socket.routes.play();
|
||||
} else {
|
||||
// YoutubemusicAPI.routes.play.actions.do();
|
||||
this.req("post", "/api/v1/pause");
|
||||
// this.socket.routes.pause();
|
||||
}
|
||||
if (!this.ensureSocketReady()) return;
|
||||
this.socket.routes[playing ? "play" : "pause"]();
|
||||
this.isPlaying = playing;
|
||||
}
|
||||
switchRepeat() {
|
||||
// this.socket.routes.repeat(nextRepeat(this.repeat));
|
||||
this.req("post", "/api/v1/switch-repeat", {
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: {
|
||||
iteration: 1
|
||||
}
|
||||
});
|
||||
setRepeat(state: Repeat) {
|
||||
if (!this.ensureSocketReady()) return;
|
||||
this.socket.routes.repeat(state);
|
||||
this.repeat = state;
|
||||
this.emitChange();
|
||||
}
|
||||
shuffle() {
|
||||
// this.socket.routes.shuffle();
|
||||
this.req("post", "/api/v1/shuffle");
|
||||
setShuffle(state: boolean) {
|
||||
if (!this.ensureSocketReady()) return;
|
||||
this.socket.routes.shuffle();
|
||||
this.shuffle = state;
|
||||
this.emitChange();
|
||||
}
|
||||
seek(ms: number) {
|
||||
// this.socket.routes.seek(ms / 1000);
|
||||
this.req("post", "/api/v1/seek-to", {
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: {
|
||||
seconds: Math.floor(ms / 1000)
|
||||
}
|
||||
});
|
||||
if (!this.ensureSocketReady()) return;
|
||||
this.socket.routes.seek(Math.round(ms / 1000));
|
||||
}
|
||||
|
||||
private req(method: "post" | "get" | "put", route: string, data: any = {}) {
|
||||
const { apiServerUrl } = Settings.plugins.YouTubeMusicControls;
|
||||
if (apiServerUrl === "") return;
|
||||
console.log(fetch(apiServerUrl + route, {
|
||||
method,
|
||||
...data,
|
||||
...(data.body && { body: JSON.stringify(data.body) })
|
||||
}));
|
||||
private ensureSocketReady(): boolean {
|
||||
if (!this.socket || !this.socket.ready) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const store = new YoutubeMusicStore(FluxDispatcher);
|
||||
|
||||
logger.info("Youtube Music Controls initialized");
|
||||
|
||||
return store;
|
||||
});
|
||||
|
||||
function restartSocket() {
|
||||
YoutubeMusicStore.socket.reconnect();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,13 +82,6 @@ const settings = definePluginSettings({
|
|||
placeholder: "ws://localhost:26539",
|
||||
default: "ws://localhost:26539",
|
||||
isValid: isUrlValid,
|
||||
},
|
||||
apiServerUrl: {
|
||||
description: "The api server url",
|
||||
type: OptionType.STRING,
|
||||
placeholder: "http://localhost:26538",
|
||||
default: "http://localhost:26538",
|
||||
isValid: isUrlValid,
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -34,26 +34,26 @@
|
|||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#vc-ytm-progress-bar>[class^="slider"] {
|
||||
#vc-ytm-progress-bar > [class^="slider"] {
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#vc-ytm-progress-bar>[class^="slider"] [class^="bar"] {
|
||||
#vc-ytm-progress-bar > [class^="slider"] [class^="bar"] {
|
||||
height: 3px !important;
|
||||
top: calc(12px - 4px / 2 + var(--bar-offset));
|
||||
}
|
||||
|
||||
#vc-ytm-progress-bar>[class^="slider"] [class^="barFill"] {
|
||||
#vc-ytm-progress-bar > [class^="slider"] [class^="barFill"] {
|
||||
background-image: var(--vc-ytm-slider-gradient);
|
||||
}
|
||||
|
||||
#vc-ytm-progress-bar>[class^="slider"]:hover [class^="barFill"] {
|
||||
#vc-ytm-progress-bar > [class^="slider"]:hover [class^="barFill"] {
|
||||
background-image: var(--vc-ytm-slider-gradient);
|
||||
}
|
||||
|
||||
#vc-ytm-progress-bar>[class^="slider"] [class^="grabber"] {
|
||||
#vc-ytm-progress-bar > [class^="slider"] [class^="grabber"] {
|
||||
background-color: var(--vc-ytm-slider-grabber);
|
||||
width: 12px !important;
|
||||
height: 12px !important;
|
||||
|
|
@ -65,7 +65,7 @@
|
|||
transition: opacity 0.1s;
|
||||
}
|
||||
|
||||
#vc-ytm-progress-bar:hover>[class^="slider"] [class^="grabber"] {
|
||||
#vc-ytm-progress-bar:hover > [class^="slider"] [class^="grabber"] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
|
@ -78,8 +78,8 @@
|
|||
}
|
||||
|
||||
.vc-ytm-button-row {
|
||||
margin-top: 14px;
|
||||
justify-content: center;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.vc-ytm-button {
|
||||
|
|
@ -93,6 +93,7 @@
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.vc-ytm-button:hover {
|
||||
|
|
@ -105,8 +106,8 @@
|
|||
width: 24px;
|
||||
}
|
||||
|
||||
[class*="shuffle"]>svg,
|
||||
[class*="repeat"]>svg {
|
||||
[class*="shuffle"] > svg,
|
||||
[class*="repeat"] > svg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@
|
|||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#vc-spotify-progress-bar>[class^="slider"] {
|
||||
#vc-spotify-progress-bar > [class^="slider"] {
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
padding: 0 !important;
|
||||
|
|
@ -186,7 +186,7 @@
|
|||
background-color: var(--vc-spotify-green);
|
||||
}
|
||||
|
||||
#vc-spotify-progress-bar>[class^="slider"] [class^="grabber"] {
|
||||
#vc-spotify-progress-bar > [class^="slider"] [class^="grabber"] {
|
||||
/* these importants are necessary, it applies a width and height through inline styles */
|
||||
height: 16px !important;
|
||||
width: 16px !important;
|
||||
|
|
@ -199,7 +199,7 @@
|
|||
transition: opacity 0.1s;
|
||||
}
|
||||
|
||||
#vc-spotify-progress-bar:hover>[class^="slider"] [class^="grabber"] {
|
||||
#vc-spotify-progress-bar:hover > [class^="slider"] [class^="grabber"] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
|
@ -226,4 +226,4 @@
|
|||
.vc-spotify-fallback {
|
||||
padding: 0.5em;
|
||||
color: var(--text-default);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue