Skip to content

Commit 6f70d17

Browse files
committed
fix: fixed an issue that caused infinite loops when using Music Together
fix #1752
1 parent 62a86e9 commit 6f70d17

File tree

3 files changed

+40
-27
lines changed

3 files changed

+40
-27
lines changed

src/plugins/music-together/index.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import { t } from '@/i18n';
66
import { createPlugin } from '@/utils';
77
import promptOptions from '@/providers/prompt-options';
88

9-
import { AppAPI, getDefaultProfile, Permission, Profile, VideoData } from './types';
9+
import { type AppElement, getDefaultProfile, type Permission, type Profile, type VideoData } from './types';
1010
import { Queue } from './queue';
11-
import { Connection, ConnectionEventUnion } from './connection';
11+
import { Connection, type ConnectionEventUnion } from './connection';
1212
import { createHostPopup } from './ui/host';
1313
import { createGuestPopup } from './ui/guest';
1414
import { createSettingPopup } from './ui/setting';
@@ -41,7 +41,7 @@ export default createPlugin<
4141
{
4242
connection?: Connection;
4343
ipc?: RendererContext<never>['ipc'];
44-
api: HTMLElement & AppAPI | null;
44+
api: AppElement | null;
4545
queue?: Queue;
4646
playerApi?: YoutubePlayer;
4747
showPrompt: (title: string, label: string) => Promise<string>;
@@ -557,7 +557,7 @@ export default createPlugin<
557557
start({ ipc }) {
558558
this.ipc = ipc;
559559
this.showPrompt = async (title: string, label: string) => ipc.invoke('music-together:prompt', title, label) as Promise<string>;
560-
this.api = document.querySelector<HTMLElement & AppAPI>('ytmusic-app');
560+
this.api = document.querySelector<AppElement>('ytmusic-app');
561561

562562
/* setup */
563563
document.querySelector('#right-content > ytmusic-settings-button')?.insertAdjacentHTML('beforebegin', settingHTML);

src/plugins/music-together/queue/queue.ts

+25-19
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { getMusicQueueRenderer } from './song';
22
import { mapQueueItem } from './utils';
33

4-
import { ConnectionEventUnion } from '@/plugins/music-together/connection';
54
import { t } from '@/i18n';
65

7-
import type { Profile, QueueAPI, VideoData } from '../types';
6+
import type { ConnectionEventUnion } from '@/plugins/music-together/connection';
7+
import type { Profile, QueueElement, VideoData } from '../types';
88
import type { QueueItem } from '@/types/datahost-get-state';
99

1010
const getHeaderPayload = (() => {
@@ -103,26 +103,29 @@ const getHeaderPayload = (() => {
103103
export type QueueOptions = {
104104
videoList?: VideoData[];
105105
owner?: Profile;
106-
queue?: HTMLElement & QueueAPI;
106+
queue?: QueueElement;
107107
getProfile: (id: string) => Profile | undefined;
108108
}
109109
export type QueueEventListener = (event: ConnectionEventUnion) => void;
110110

111111
export class Queue {
112-
private queue: (HTMLElement & QueueAPI);
112+
private readonly queue: QueueElement;
113+
113114
private originalDispatch?: (obj: {
114115
type: string;
115116
payload?: { items?: QueueItem[] | undefined; };
116117
}) => void;
118+
117119
private internalDispatch = false;
118120
private ignoreFlag = false;
119121
private listeners: QueueEventListener[] = [];
120-
private owner: Profile | null = null;
121-
private getProfile: (id: string) => Profile | undefined;
122+
123+
private owner: Profile | null;
124+
private readonly getProfile: (id: string) => Profile | undefined;
122125

123126
constructor(options: QueueOptions) {
124127
this.getProfile = options.getProfile;
125-
this.queue = options.queue ?? document.querySelector<HTMLElement & QueueAPI>('#queue')!;
128+
this.queue = options.queue ?? (document.querySelector<QueueElement>('#queue')!);
126129
this.owner = options.owner ?? null;
127130
this._videoList = options.videoList ?? [];
128131
}
@@ -135,11 +138,11 @@ export class Queue {
135138
}
136139

137140
get selectedIndex() {
138-
return mapQueueItem((it) => it?.selected, this.queue.store.getState().queue.items).findIndex(Boolean) ?? 0;
141+
return mapQueueItem((it) => it?.selected, this.queue.queue.store.store.getState().queue.items).findIndex(Boolean) ?? 0;
139142
}
140143

141144
get rawItems() {
142-
return this.queue?.store.getState().queue.items;
145+
return this.queue?.queue.store.store.getState().queue.items;
143146
}
144147

145148
get flatItems() {
@@ -169,8 +172,8 @@ export class Queue {
169172
this.queue?.dispatch({
170173
type: 'ADD_ITEMS',
171174
payload: {
172-
nextQueueItemId: this.queue.store.getState().queue.nextQueueItemId,
173-
index: index ?? this.queue.store.getState().queue.items.length ?? 0,
175+
nextQueueItemId: this.queue.queue.store.store.getState().queue.nextQueueItemId,
176+
index: index ?? this.queue.queue.store.store.getState().queue.items.length ?? 0,
174177
items,
175178
shuffleEnabled: false,
176179
shouldAssignIds: true
@@ -249,7 +252,7 @@ export class Queue {
249252
return;
250253
}
251254

252-
if (this.originalDispatch) this.queue.store.dispatch = this.originalDispatch;
255+
if (this.originalDispatch) this.queue.queue.store.store.dispatch = this.originalDispatch;
253256
}
254257

255258
injection() {
@@ -258,8 +261,8 @@ export class Queue {
258261
return;
259262
}
260263

261-
this.originalDispatch = this.queue.store.dispatch;
262-
this.queue.store.dispatch = (event) => {
264+
this.originalDispatch = this.queue.queue.store.store.dispatch;
265+
this.queue.queue.store.store.dispatch = (event) => {
263266
if (!this.queue || !this.owner) {
264267
console.error('Queue is not initialized!');
265268
return;
@@ -361,10 +364,13 @@ export class Queue {
361364

362365
const fakeContext = {
363366
...this.queue,
364-
store: {
365-
...this.queue.store,
366-
dispatch: this.originalDispatch
367-
}
367+
queue: {
368+
...this.queue.queue,
369+
store: {
370+
...this.queue.queue.store,
371+
dispatch: this.originalDispatch,
372+
}
373+
},
368374
};
369375
this.originalDispatch?.call(fakeContext, event);
370376
};
@@ -400,7 +406,7 @@ export class Queue {
400406
type: 'UPDATE_ITEMS',
401407
payload: {
402408
items: items,
403-
nextQueueItemId: this.queue.store.getState().queue.nextQueueItemId,
409+
nextQueueItemId: this.queue.queue.store.store.getState().queue.nextQueueItemId,
404410
shouldAssignIds: true,
405411
currentIndex: -1
406412
}

src/plugins/music-together/types.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { YoutubePlayer } from '@/types/youtube-player';
2-
import { GetState, QueueItem } from '@/types/datahost-get-state';
1+
import type { YoutubePlayer } from '@/types/youtube-player';
2+
import type { GetState, QueueItem } from '@/types/datahost-get-state';
33

44
type StoreState = GetState;
55
type Store = {
@@ -14,16 +14,23 @@ type Store = {
1414
replaceReducer: (param1: unknown) => unknown;
1515
subscribe: (callback: () => void) => unknown;
1616
}
17-
export type QueueAPI = {
17+
18+
export type QueueElement = HTMLElement & {
1819
dispatch(obj: {
1920
type: string;
2021
payload?: unknown;
2122
}): void;
23+
queue: QueueAPI;
24+
};
25+
export type QueueAPI = {
2226
getItems(): unknown[];
23-
store: Store;
27+
store: {
28+
store: Store,
29+
};
2430
continuation?: string;
2531
autoPlaying?: boolean;
2632
};
33+
export type AppElement = HTMLElement & AppAPI;
2734
export type AppAPI = {
2835
queue_: QueueAPI;
2936
playerApi_: YoutubePlayer;

0 commit comments

Comments
 (0)