Cleanup & Normalize Endings

This commit is contained in:
2026-04-07 18:02:07 +10:00
parent 83ef103118
commit b28e245019
2 changed files with 61 additions and 49 deletions
+7
View File
@@ -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
+54 -49
View File
@@ -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);