mirror of
https://github.com/Mastermindzh/tidal-hifi.git
synced 2025-05-15 07:12:59 +02:00
189 lines
4.5 KiB
TypeScript
189 lines
4.5 KiB
TypeScript
export function getTidalReduxStore() {
|
|
// Find the react container
|
|
let reactContainer: Record<string, unknown> | null = null;
|
|
for (const child of document.body?.children ?? []) {
|
|
const container = Object.entries(child).find(([key]) => key.startsWith("__reactContainer$"));
|
|
// console.log(container);
|
|
if (!container) continue;
|
|
reactContainer = container[1];
|
|
break;
|
|
}
|
|
if (!reactContainer) {
|
|
throw new Error("Could not find React root");
|
|
}
|
|
// Traverse the react tree until we find the redux store
|
|
const seen = new Set();
|
|
const queue = [reactContainer];
|
|
let store;
|
|
|
|
const properties = ["children", "child", "pendingProps", "memoizedProps", "props"];
|
|
while (!store && queue.length) {
|
|
const node = queue.shift();
|
|
if (!node) break;
|
|
if (
|
|
"store" in node &&
|
|
typeof node.store === "object" &&
|
|
node.store !== null &&
|
|
"getState" in node.store &&
|
|
typeof node.store.getState === "function"
|
|
) {
|
|
store = node.store;
|
|
break;
|
|
}
|
|
for (const property of properties) {
|
|
const value = node[property];
|
|
if (typeof value === "object" && value !== null) {
|
|
if (seen.has(value)) continue;
|
|
seen.add(value);
|
|
queue.push(value as Record<string, unknown>);
|
|
}
|
|
}
|
|
}
|
|
if (!store) throw new Error("Could not find Redux store");
|
|
return store as TidalReduxStore;
|
|
}
|
|
|
|
export type TidalReduxStore = {
|
|
getState: () => ReduxState;
|
|
dispatch: (action: Action) => void;
|
|
subscribe: (listener: () => void) => () => void;
|
|
};
|
|
|
|
export type ReduxState = {
|
|
[key: string]: unknown;
|
|
content: {
|
|
mediaItems: Record<string, MediaItem>;
|
|
};
|
|
favorites: {
|
|
albums: number[];
|
|
artists: number[];
|
|
mixes: number[];
|
|
playlists: number[];
|
|
tracks: number[];
|
|
users: number[];
|
|
videos: number[];
|
|
};
|
|
playbackControls: {
|
|
desiredPlaybackState: "NOT_PLAYING" | "PLAYING" | "IDLE" | string;
|
|
latestCurrentTime: number;
|
|
latestCurrentTimeSyncTimestamp: number;
|
|
muted: boolean;
|
|
playbackState: "NOT_PLAYING" | "PLAYING" | "IDLE" | "STALLED";
|
|
startAt: number;
|
|
volume: number;
|
|
volumeUnmute: number;
|
|
mediaProduct: {
|
|
productId: string;
|
|
productType: "track" | string;
|
|
sourceId: string;
|
|
sourceType: "PLAYLIST" | string;
|
|
};
|
|
};
|
|
playQueue: {
|
|
shuffleModeEnabled: boolean;
|
|
repeatMode: RepeatMode;
|
|
};
|
|
};
|
|
|
|
export const enum RepeatMode {
|
|
REPEAT_OFF = 0,
|
|
REPEAT_ALL = 1,
|
|
REPEAT_SINGLE = 2,
|
|
}
|
|
type MediaItem =
|
|
| {
|
|
type: "track";
|
|
item: {
|
|
album: {
|
|
id: number;
|
|
title: string;
|
|
cover: string;
|
|
vibrantColor: string;
|
|
releaseDate: string;
|
|
};
|
|
artist: Artist;
|
|
artists: Array<Artist>;
|
|
audioModes: Array<"STEREO" | string>;
|
|
audioQuality: "LOSSLESS" | string;
|
|
bpm: number | null;
|
|
copyright: string;
|
|
dateAdded: string;
|
|
description: string | null;
|
|
duration: number;
|
|
explicit: boolean;
|
|
id: number;
|
|
isrc: string;
|
|
itemUuid: string;
|
|
peak: number;
|
|
popularity: number;
|
|
title: string;
|
|
trackNumber: number;
|
|
url: string;
|
|
};
|
|
}
|
|
| {
|
|
type: "video";
|
|
item: {
|
|
artists: Array<Artist>;
|
|
contentType: "video";
|
|
duration: number;
|
|
id: number;
|
|
imageId: string;
|
|
explicit: boolean;
|
|
title: string;
|
|
type: string;
|
|
url: string;
|
|
vibrantColor: string;
|
|
};
|
|
};
|
|
|
|
type Artist = {
|
|
id: number;
|
|
name: string;
|
|
type: "MAIN" | string;
|
|
picture: string;
|
|
};
|
|
|
|
type Action =
|
|
| {
|
|
type:
|
|
| "playbackControls/PAUSE"
|
|
| "playbackControls/PLAY"
|
|
| "playbackControls/STOP"
|
|
| "playbackControls/SKIP_PREVIOUS"
|
|
| "playbackControls/SKIP_NEXT"
|
|
| "playQueue/TOGGLE_SHUFFLE"
|
|
| "playQueue/TOGGLE_REPEAT_MODE"
|
|
| "ROUTER_GO_BACK"
|
|
| "ROUTER_GO_FORWARD";
|
|
}
|
|
| {
|
|
type: "playbackControls/SET_VOLUME";
|
|
payload: {
|
|
/** 0 - 100 */
|
|
volume: number;
|
|
};
|
|
}
|
|
| {
|
|
type: "playbackControls/SET_MUTE";
|
|
payload: {
|
|
mute: boolean;
|
|
};
|
|
}
|
|
| {
|
|
type: "ROUTER_PUSH";
|
|
payload: {
|
|
pathname: string;
|
|
options: Record<string, unknown>;
|
|
hash: string;
|
|
};
|
|
}
|
|
| {
|
|
type: "content/TOGGLE_FAVORITE_ITEMS";
|
|
payload: {
|
|
from: "heart";
|
|
items: Array<{ itemId: number; itemType: "track" }>;
|
|
moduleId?: string;
|
|
};
|
|
};
|