mirror of
https://github.com/meowarex/TidaLuna-Plugins.git
synced 2026-06-17 19:33:10 +10:00
Cleanup & Normalize Endings
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
*.ts text eol=lf
|
||||||
|
*.tsx text eol=lf
|
||||||
|
*.js text eol=lf
|
||||||
|
*.css text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
@@ -1668,24 +1668,6 @@ const getReduxState = (preferOriginal = false): any => {
|
|||||||
return redux.store.getState() as any;
|
return redux.store.getState() as any;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Tidal Connect / cast: Luna PlayState often does not track remote playback; Redux matches the in-app progress bar. */
|
|
||||||
const isRemotePlayback = (state = getReduxState()): boolean => {
|
|
||||||
if (state?.activePlayer?.activePlayer === "REMOTE_PLAYBACK") return true;
|
|
||||||
return state?.playbackControls?.playbackContext?.playbackSessionId === "tidal-connect";
|
|
||||||
};
|
|
||||||
|
|
||||||
const reduxPlaybackIsPlaying = (state = getReduxState()): boolean => {
|
|
||||||
const pc = state?.playbackControls;
|
|
||||||
if (!pc) return false;
|
|
||||||
if (pc.playbackState === "NOT_PLAYING") return false;
|
|
||||||
if (pc.playbackState === "PLAYING") return true;
|
|
||||||
const apt = state?.accumulatedPlaybackTime?.playbackState;
|
|
||||||
if (apt === "NOT_PLAYING") return false;
|
|
||||||
if (apt === "PLAYING") return true;
|
|
||||||
// Connect often leaves playbackState on IDLE while desiredPlaybackState is PLAYING.
|
|
||||||
return pc.desiredPlaybackState === "PLAYING";
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNativeTrackEntity = (trackId: string): any | null =>
|
const getNativeTrackEntity = (trackId: string): any | null =>
|
||||||
getReduxState(true)?.entities?.tracks?.entities?.[trackId] ?? null;
|
getReduxState(true)?.entities?.tracks?.entities?.[trackId] ?? null;
|
||||||
|
|
||||||
@@ -2073,7 +2055,6 @@ let scrollAnimPending: {
|
|||||||
} | null = null;
|
} | null = null;
|
||||||
let scrollUnlockTimeout: LunaUnload | null = null;
|
let scrollUnlockTimeout: LunaUnload | null = null;
|
||||||
let scrollCleanupTimeout: LunaUnload | null = null;
|
let scrollCleanupTimeout: LunaUnload | null = null;
|
||||||
let postTrackChangeResyncTimeout: LunaUnload | null = null;
|
|
||||||
let animatingEls: HTMLElement[] = [];
|
let animatingEls: HTMLElement[] = [];
|
||||||
|
|
||||||
const clearScrollAnim = (): void => {
|
const clearScrollAnim = (): void => {
|
||||||
@@ -2095,13 +2076,6 @@ const clearScrollAnim = (): void => {
|
|||||||
scrollAnimPending = null;
|
scrollAnimPending = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearPostTrackChangeResync = (): void => {
|
|
||||||
if (postTrackChangeResyncTimeout) {
|
|
||||||
postTrackChangeResyncTimeout();
|
|
||||||
postTrackChangeResyncTimeout = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const applyScrollBounce = (
|
const applyScrollBounce = (
|
||||||
scrollParent: HTMLElement,
|
scrollParent: HTMLElement,
|
||||||
referenceIdx: number,
|
referenceIdx: number,
|
||||||
@@ -2225,16 +2199,57 @@ let savedScroll: any = null;
|
|||||||
let savedScrollBy: any = null;
|
let savedScrollBy: any = null;
|
||||||
let scrollAllowed = false;
|
let scrollAllowed = false;
|
||||||
|
|
||||||
|
// MARKER: Tidal Connect (Casting & Remote Lyrics Syncing)
|
||||||
|
// Uses Redux to mirror Seek Bar progress because Luna PlayState doesn't track remote playback.
|
||||||
|
const isRemotePlayback = (state = getReduxState()): boolean => {
|
||||||
|
if (state?.activePlayer?.activePlayer === "REMOTE_PLAYBACK") return true;
|
||||||
|
return state?.playbackControls?.playbackContext?.playbackSessionId === "tidal-connect";
|
||||||
|
};
|
||||||
|
|
||||||
|
const reduxPlaybackIsPlaying = (state = getReduxState()): boolean => {
|
||||||
|
const pc = state?.playbackControls;
|
||||||
|
if (!pc) return false;
|
||||||
|
if (pc.playbackState === "NOT_PLAYING") return false;
|
||||||
|
if (pc.playbackState === "PLAYING") return true;
|
||||||
|
const apt = state?.accumulatedPlaybackTime?.playbackState;
|
||||||
|
if (apt === "NOT_PLAYING") return false;
|
||||||
|
if (apt === "PLAYING") return true;
|
||||||
|
// Tidal Connect leaves playbackState on IDLE (that's why using desiredPlaybackState)
|
||||||
|
return pc.desiredPlaybackState === "PLAYING";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPrimaryArtistName = (value: unknown): string => {
|
||||||
|
if (!Array.isArray(value) || value.length === 0) return "";
|
||||||
|
const first = value[0] as { name?: unknown; artist?: { name?: unknown } } | undefined;
|
||||||
|
if (!first) return "";
|
||||||
|
if (typeof first.name === "string") return first.name;
|
||||||
|
if (typeof first.artist?.name === "string") return first.artist.name;
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const trackInfoFromReduxProductId = (productId: string): TrackInfo | null => {
|
||||||
|
const ent = getNativeTrackEntity(productId);
|
||||||
|
if (!ent) return null;
|
||||||
|
const attr = ent.attributes ?? ent;
|
||||||
|
const title = String(attr.title ?? "");
|
||||||
|
const artist = String(attr.artist?.name ?? getPrimaryArtistName(attr.artists) ?? "");
|
||||||
|
const isrc = attr.isrc ?? undefined;
|
||||||
|
if (!title || !artist) return null;
|
||||||
|
return { trackId: productId, title, artist, isrc };
|
||||||
|
};
|
||||||
|
|
||||||
// playback time in ms (interpolated between currentTime updates)
|
// playback time in ms (interpolated between currentTime updates)
|
||||||
let lastPlayerTime = 0;
|
let lastPlayerTime = 0;
|
||||||
let lastPlayerTimeAt = 0;
|
let lastPlayerTimeAt = 0;
|
||||||
let wasPlaying = false;
|
let wasPlaying = false;
|
||||||
|
|
||||||
// Remote playback: same interpolation idea, fed from Redux playbackControls.latestCurrentTime (seconds).
|
// Remote playback time in seconds (interpolation [Redux playbackControls.latestCurrentTime])
|
||||||
let lastRemotePlayerTime = 0;
|
let lastRemotePlayerTime = 0;
|
||||||
let lastRemotePlayerTimeAt = 0;
|
let lastRemotePlayerTimeAt = 0;
|
||||||
let wasRemotePlaying = false;
|
let wasRemotePlaying = false;
|
||||||
|
|
||||||
|
let postTrackChangeResyncTimeout: LunaUnload | null = null;
|
||||||
|
|
||||||
const getRemotePlaybackMs = (state = getReduxState()): number => {
|
const getRemotePlaybackMs = (state = getReduxState()): number => {
|
||||||
const pc = state?.playbackControls;
|
const pc = state?.playbackControls;
|
||||||
const raw = Number(pc?.latestCurrentTime);
|
const raw = Number(pc?.latestCurrentTime);
|
||||||
@@ -2294,7 +2309,7 @@ const getPlaybackMs = (): number => {
|
|||||||
return playerTime * 1000;
|
return playerTime * 1000;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Re-anchor lyric time to the same clock as getPlaybackMs (PlayState or Redux on Connect). */
|
/** Re Sync Lyrics to getPlaybackMs (PlayState/Redux on Connect). */
|
||||||
const snapPlaybackInterpolationToPlayer = (): void => {
|
const snapPlaybackInterpolationToPlayer = (): void => {
|
||||||
const state = getReduxState();
|
const state = getReduxState();
|
||||||
if (isRemotePlayback(state)) {
|
if (isRemotePlayback(state)) {
|
||||||
@@ -2313,13 +2328,11 @@ const snapPlaybackInterpolationToPlayer = (): void => {
|
|||||||
const hasCurrentSyncAnchor = (): boolean =>
|
const hasCurrentSyncAnchor = (): boolean =>
|
||||||
primaryLineIdx >= 0 && primaryLineIdx < lines.length && activeLineIdxs.size > 0;
|
primaryLineIdx >= 0 && primaryLineIdx < lines.length && activeLineIdxs.size > 0;
|
||||||
|
|
||||||
const getPrimaryArtistName = (value: unknown): string => {
|
const clearPostTrackChangeResync = (): void => {
|
||||||
if (!Array.isArray(value) || value.length === 0) return "";
|
if (postTrackChangeResyncTimeout) {
|
||||||
const first = value[0] as { name?: unknown; artist?: { name?: unknown } } | undefined;
|
postTrackChangeResyncTimeout();
|
||||||
if (!first) return "";
|
postTrackChangeResyncTimeout = null;
|
||||||
if (typeof first.name === "string") return first.name;
|
}
|
||||||
if (typeof first.artist?.name === "string") return first.artist.name;
|
|
||||||
return "";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const scheduleRemoteTrackChangeResync = (attempt = 0): void => {
|
const scheduleRemoteTrackChangeResync = (attempt = 0): void => {
|
||||||
@@ -2347,17 +2360,6 @@ const scheduleRemoteTrackChangeResync = (attempt = 0): void => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const trackInfoFromReduxProductId = (productId: string): TrackInfo | null => {
|
|
||||||
const ent = getNativeTrackEntity(productId);
|
|
||||||
if (!ent) return null;
|
|
||||||
const attr = ent.attributes ?? ent;
|
|
||||||
const title = String(attr.title ?? "");
|
|
||||||
const artist = String(attr.artist?.name ?? getPrimaryArtistName(attr.artists) ?? "");
|
|
||||||
const isrc = attr.isrc ?? undefined;
|
|
||||||
if (!title || !artist) return null;
|
|
||||||
return { trackId: productId, title, artist, isrc };
|
|
||||||
};
|
|
||||||
|
|
||||||
// get title + artist from media item (Used everywhere now <3)
|
// get title + artist from media item (Used everywhere now <3)
|
||||||
const getTrackInfo = async (): Promise<TrackInfo | null> => {
|
const getTrackInfo = async (): Promise<TrackInfo | null> => {
|
||||||
const mi = await MediaItem.fromPlaybackContext();
|
const mi = await MediaItem.fromPlaybackContext();
|
||||||
@@ -2386,6 +2388,9 @@ const getTrackInfo = async (): Promise<TrackInfo | null> => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// MARKER: Lyrics API Fetching (Caching & Romanization)
|
||||||
|
|
||||||
// fetch syllables from the API (wiped on track change)
|
// fetch syllables from the API (wiped on track change)
|
||||||
let cachedLyricsKey: string | null = null;
|
let cachedLyricsKey: string | null = null;
|
||||||
let cachedLyricsData: LyricsApiResponse | null = null;
|
let cachedLyricsData: LyricsApiResponse | null = null;
|
||||||
@@ -3490,7 +3495,7 @@ const clearTickLoop = (): void => {
|
|||||||
const teardown = (): void => {
|
const teardown = (): void => {
|
||||||
trackChangeToken++;
|
trackChangeToken++;
|
||||||
clearTickLoop();
|
clearTickLoop();
|
||||||
clearPostTrackChangeResync();
|
clearPostTrackChangeResync(); // Tidal Connect (see MARKER block)
|
||||||
stopTidalFollowLoop();
|
stopTidalFollowLoop();
|
||||||
clearScrollAnim();
|
clearScrollAnim();
|
||||||
unwatchRerender();
|
unwatchRerender();
|
||||||
@@ -4132,7 +4137,7 @@ const onTrackChange = async (): Promise<void> => {
|
|||||||
lines = result.lines;
|
lines = result.lines;
|
||||||
watchForRerender();
|
watchForRerender();
|
||||||
startTickLoop();
|
startTickLoop();
|
||||||
scheduleRemoteTrackChangeResync();
|
scheduleRemoteTrackChangeResync(); // Tidal Connect (see MARKER block)
|
||||||
} else {
|
} else {
|
||||||
safeTimeout(unloads, () => {
|
safeTimeout(unloads, () => {
|
||||||
if (token !== trackChangeToken) return;
|
if (token !== trackChangeToken) return;
|
||||||
@@ -4158,7 +4163,7 @@ const onTrackChange = async (): Promise<void> => {
|
|||||||
lines = result.lines;
|
lines = result.lines;
|
||||||
watchForRerender();
|
watchForRerender();
|
||||||
startTickLoop();
|
startTickLoop();
|
||||||
scheduleRemoteTrackChangeResync();
|
scheduleRemoteTrackChangeResync(); // Tidal Connect (see MARKER block)
|
||||||
}
|
}
|
||||||
} else if (++panelRetries < 20) {
|
} else if (++panelRetries < 20) {
|
||||||
safeTimeout(unloads, waitForPanel, 250);
|
safeTimeout(unloads, waitForPanel, 250);
|
||||||
|
|||||||
Reference in New Issue
Block a user