Include lyric simplification, restore clear button
- merges lyric simplification - restores metadata/clear
This commit is contained in:
parent
f92cd89c46
commit
8835fc640a
6 changed files with 63 additions and 31 deletions
|
@ -1036,6 +1036,18 @@ export type InternetProviderLyricResponse = {
|
||||||
source: string;
|
source: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SynchronizedLyricMetadata = {
|
||||||
|
lyrics: SynchronizedLyricsArray;
|
||||||
|
remote: boolean;
|
||||||
|
} & Omit<InternetProviderLyricResponse, 'lyrics'>;
|
||||||
|
|
||||||
|
export type UnsyhchronizedLyricMetadata = {
|
||||||
|
lyrics: string;
|
||||||
|
remote: boolean;
|
||||||
|
} & Omit<InternetProviderLyricResponse, 'lyrics'>;
|
||||||
|
|
||||||
|
export type FullLyricsMetadata = SynchronizedLyricMetadata | UnsyhchronizedLyricMetadata;
|
||||||
|
|
||||||
export type LyricOverride = Omit<InternetProviderLyricResponse, 'lyrics'>;
|
export type LyricOverride = Omit<InternetProviderLyricResponse, 'lyrics'>;
|
||||||
|
|
||||||
export const instanceOfCancellationError = (error: any) => {
|
export const instanceOfCancellationError = (error: any) => {
|
||||||
|
|
|
@ -6,10 +6,10 @@ const LyricClearButton = styled(Button)`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
top: 7vh;
|
bottom: 6vh;
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
top: 5vh;
|
bottom: 3vh;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ export const LyricSkip = ({ onClick }: LyricSkipProps) => {
|
||||||
<LyricClearButton
|
<LyricClearButton
|
||||||
leftIcon={<RiCloseFill />}
|
leftIcon={<RiCloseFill />}
|
||||||
size="xl"
|
size="xl"
|
||||||
tooltip={{ label: 'Remove incorrect lyrics', position: 'bottom' }}
|
tooltip={{ label: 'Remove incorrect lyrics', position: 'top' }}
|
||||||
variant="default"
|
variant="default"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import { Center, Group } from '@mantine/core';
|
import { Center, Group } from '@mantine/core';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { ErrorBoundary } from 'react-error-boundary';
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
|
@ -9,6 +10,7 @@ import { ScrollArea, Spinner, TextTitle } from '/@/renderer/components';
|
||||||
import { ErrorFallback } from '/@/renderer/features/action-required';
|
import { ErrorFallback } from '/@/renderer/features/action-required';
|
||||||
import { UnsynchronizedLyrics } from '/@/renderer/features/lyrics/unsynchronized-lyrics';
|
import { UnsynchronizedLyrics } from '/@/renderer/features/lyrics/unsynchronized-lyrics';
|
||||||
import { getServerById, useCurrentSong } from '/@/renderer/store';
|
import { getServerById, useCurrentSong } from '/@/renderer/store';
|
||||||
|
import { FullLyricsMetadata, SynchronizedLyricMetadata } from '/@/renderer/api/types';
|
||||||
|
|
||||||
const LyricsScrollContainer = styled(motion(ScrollArea))`
|
const LyricsScrollContainer = styled(motion(ScrollArea))`
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
@ -36,10 +38,18 @@ const LyricsScrollContainer = styled(motion(ScrollArea))`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
function isSynchronized(data: FullLyricsMetadata): data is SynchronizedLyricMetadata {
|
||||||
|
// Type magic. The only difference between Synchronized and Unsynchhronized is
|
||||||
|
// the datatype of lyrics. This makes Typescript happier later...
|
||||||
|
return Array.isArray(data.lyrics);
|
||||||
|
}
|
||||||
|
|
||||||
export const Lyrics = () => {
|
export const Lyrics = () => {
|
||||||
const currentSong = useCurrentSong();
|
const currentSong = useCurrentSong();
|
||||||
const currentServer = getServerById(currentSong?.serverId);
|
const currentServer = getServerById(currentSong?.serverId);
|
||||||
|
|
||||||
|
const [clear, setClear] = useState(false);
|
||||||
|
|
||||||
const { data, isLoading } = useSongLyrics(
|
const { data, isLoading } = useSongLyrics(
|
||||||
{
|
{
|
||||||
query: { songId: currentSong?.id || '' },
|
query: { songId: currentSong?.id || '' },
|
||||||
|
@ -48,6 +58,11 @@ export const Lyrics = () => {
|
||||||
currentSong,
|
currentSong,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// We want to reset the clear flag whenever a song changes
|
||||||
|
setClear(false);
|
||||||
|
}, [currentSong]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
@ -55,7 +70,7 @@ export const Lyrics = () => {
|
||||||
container
|
container
|
||||||
size={25}
|
size={25}
|
||||||
/>
|
/>
|
||||||
) : !data?.lyrics ? (
|
) : !data?.lyrics || clear ? (
|
||||||
<Center>
|
<Center>
|
||||||
<Group>
|
<Group>
|
||||||
<RiInformationFill size="2rem" />
|
<RiInformationFill size="2rem" />
|
||||||
|
@ -74,19 +89,15 @@ export const Lyrics = () => {
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
transition={{ duration: 0.5 }}
|
transition={{ duration: 0.5 }}
|
||||||
>
|
>
|
||||||
{Array.isArray(data.lyrics) ? (
|
{isSynchronized(data) ? (
|
||||||
<SynchronizedLyrics
|
<SynchronizedLyrics
|
||||||
lyrics={data.lyrics}
|
{...data}
|
||||||
override={null}
|
onRemoveLyric={() => setClear(true)}
|
||||||
source={data.source}
|
|
||||||
onRemoveLyric={() => {}}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<UnsynchronizedLyrics
|
<UnsynchronizedLyrics
|
||||||
lyrics={data.lyrics}
|
{...data}
|
||||||
override={null}
|
onRemoveLyric={() => setClear(true)}
|
||||||
source={data.source}
|
|
||||||
onRemoveLyric={() => {}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</LyricsScrollContainer>
|
</LyricsScrollContainer>
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { UseQueryResult, useQuery } from '@tanstack/react-query';
|
||||||
import {
|
import {
|
||||||
LyricsQuery,
|
LyricsQuery,
|
||||||
QueueSong,
|
QueueSong,
|
||||||
SynchronizedLyricsArray,
|
SynchronizedLyricsArray,
|
||||||
InternetProviderLyricResponse,
|
InternetProviderLyricResponse,
|
||||||
|
FullLyricsMetadata,
|
||||||
} from '/@/renderer/api/types';
|
} from '/@/renderer/api/types';
|
||||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||||
import { getServerById, useLyricsSettings } from '/@/renderer/store';
|
import { getServerById, useLyricsSettings } from '/@/renderer/store';
|
||||||
|
@ -37,7 +38,9 @@ const formatLyrics = (lyrics: string) => {
|
||||||
return formattedLyrics;
|
return formattedLyrics;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useServerLyrics = (args: QueryHookArgs<LyricsQuery>) => {
|
export const useServerLyrics = (
|
||||||
|
args: QueryHookArgs<LyricsQuery>,
|
||||||
|
): UseQueryResult<string | null> => {
|
||||||
const { query, serverId } = args;
|
const { query, serverId } = args;
|
||||||
const server = getServerById(serverId);
|
const server = getServerById(serverId);
|
||||||
|
|
||||||
|
@ -55,7 +58,10 @@ export const useServerLyrics = (args: QueryHookArgs<LyricsQuery>) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSongLyrics = (args: QueryHookArgs<LyricsQuery>, song: QueueSong | undefined) => {
|
export const useSongLyrics = (
|
||||||
|
args: QueryHookArgs<LyricsQuery>,
|
||||||
|
song: QueueSong | undefined,
|
||||||
|
): UseQueryResult<FullLyricsMetadata> => {
|
||||||
const { query } = args;
|
const { query } = args;
|
||||||
const { fetch } = useLyricsSettings();
|
const { fetch } = useLyricsSettings();
|
||||||
const server = getServerById(song?.serverId);
|
const server = getServerById(song?.serverId);
|
||||||
|
@ -73,6 +79,7 @@ export const useSongLyrics = (args: QueryHookArgs<LyricsQuery>, song: QueueSong
|
||||||
artist: song.artists?.[0]?.name,
|
artist: song.artists?.[0]?.name,
|
||||||
lyrics: formatLyrics(song.lyrics),
|
lyrics: formatLyrics(song.lyrics),
|
||||||
name: song.name,
|
name: song.name,
|
||||||
|
remote: false,
|
||||||
source: server?.name ?? 'music server',
|
source: server?.name ?? 'music server',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -88,6 +95,7 @@ export const useSongLyrics = (args: QueryHookArgs<LyricsQuery>, song: QueueSong
|
||||||
artist: song.artists?.[0]?.name,
|
artist: song.artists?.[0]?.name,
|
||||||
lyrics: jfLyrics,
|
lyrics: jfLyrics,
|
||||||
name: song.name,
|
name: song.name,
|
||||||
|
remote: false,
|
||||||
source: server?.name ?? 'music server',
|
source: server?.name ?? 'music server',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -101,6 +109,7 @@ export const useSongLyrics = (args: QueryHookArgs<LyricsQuery>, song: QueueSong
|
||||||
return {
|
return {
|
||||||
...remoteLyricsResult,
|
...remoteLyricsResult,
|
||||||
lyrics: formatLyrics(remoteLyricsResult.lyrics),
|
lyrics: formatLyrics(remoteLyricsResult.lyrics),
|
||||||
|
remote: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { PlaybackType, PlayerStatus } from '/@/renderer/types';
|
||||||
import { LyricLine } from '/@/renderer/features/lyrics/lyric-line';
|
import { LyricLine } from '/@/renderer/features/lyrics/lyric-line';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { PlayersRef } from '/@/renderer/features/player/ref/players-ref';
|
import { PlayersRef } from '/@/renderer/features/player/ref/players-ref';
|
||||||
import { LyricOverride, SynchronizedLyricsArray } from '/@/renderer/api/types';
|
import { FullLyricsMetadata, SynchronizedLyricsArray } from '/@/renderer/api/types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { LyricSkip } from '/@/renderer/features/lyrics/lyric-skip';
|
import { LyricSkip } from '/@/renderer/features/lyrics/lyric-skip';
|
||||||
|
|
||||||
|
@ -20,17 +20,17 @@ const SynchronizedLyricsContainer = styled.div`
|
||||||
padding: 5rem 0;
|
padding: 5rem 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface SynchronizedLyricsProps {
|
interface SynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> {
|
||||||
lyrics: SynchronizedLyricsArray;
|
lyrics: SynchronizedLyricsArray;
|
||||||
onRemoveLyric: () => void;
|
onRemoveLyric: () => void;
|
||||||
override: LyricOverride | null;
|
|
||||||
source: string | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SynchronizedLyrics = ({
|
export const SynchronizedLyrics = ({
|
||||||
|
artist,
|
||||||
lyrics,
|
lyrics,
|
||||||
|
name,
|
||||||
onRemoveLyric,
|
onRemoveLyric,
|
||||||
override,
|
remote,
|
||||||
source,
|
source,
|
||||||
}: SynchronizedLyricsProps) => {
|
}: SynchronizedLyricsProps) => {
|
||||||
const playersRef = PlayersRef;
|
const playersRef = PlayersRef;
|
||||||
|
@ -275,11 +275,11 @@ export const SynchronizedLyrics = ({
|
||||||
text={`Lyrics provided by ${source}`}
|
text={`Lyrics provided by ${source}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{override && (
|
{remote && (
|
||||||
<>
|
<>
|
||||||
<LyricLine
|
<LyricLine
|
||||||
className="lyric-credit"
|
className="lyric-credit"
|
||||||
text={`(Matched as ${override.name} by ${override.artist})`}
|
text={`(Matched as ${artist} by ${name})`}
|
||||||
/>
|
/>
|
||||||
<LyricSkip onClick={removeLyric} />
|
<LyricSkip onClick={removeLyric} />
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { LyricLine } from '/@/renderer/features/lyrics/lyric-line';
|
import { LyricLine } from '/@/renderer/features/lyrics/lyric-line';
|
||||||
import { LyricOverride } from '/@/renderer/api/types';
|
import { FullLyricsMetadata } from '/@/renderer/api/types';
|
||||||
import { LyricSkip } from '/@/renderer/features/lyrics/lyric-skip';
|
import { LyricSkip } from '/@/renderer/features/lyrics/lyric-skip';
|
||||||
|
|
||||||
interface UnsynchronizedLyricsProps {
|
interface UnsynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> {
|
||||||
lyrics: string;
|
lyrics: string;
|
||||||
onRemoveLyric: () => void;
|
onRemoveLyric: () => void;
|
||||||
override: LyricOverride | null;
|
|
||||||
source: string | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const UnsynchronizedLyricsContainer = styled.div`
|
const UnsynchronizedLyricsContainer = styled.div`
|
||||||
|
@ -16,9 +14,11 @@ const UnsynchronizedLyricsContainer = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const UnsynchronizedLyrics = ({
|
export const UnsynchronizedLyrics = ({
|
||||||
onRemoveLyric,
|
artist,
|
||||||
lyrics,
|
lyrics,
|
||||||
override,
|
name,
|
||||||
|
onRemoveLyric,
|
||||||
|
remote,
|
||||||
source,
|
source,
|
||||||
}: UnsynchronizedLyricsProps) => {
|
}: UnsynchronizedLyricsProps) => {
|
||||||
const lines = useMemo(() => {
|
const lines = useMemo(() => {
|
||||||
|
@ -33,11 +33,11 @@ export const UnsynchronizedLyrics = ({
|
||||||
text={`Lyrics provided by ${source}`}
|
text={`Lyrics provided by ${source}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{override && (
|
{remote && (
|
||||||
<>
|
<>
|
||||||
<LyricLine
|
<LyricLine
|
||||||
className="lyric-credit"
|
className="lyric-credit"
|
||||||
text={`(Matched as ${override.name} by ${override.artist})`}
|
text={`(Matched as ${artist} by ${name})`}
|
||||||
/>
|
/>
|
||||||
<LyricSkip onClick={onRemoveLyric} />
|
<LyricSkip onClick={onRemoveLyric} />
|
||||||
</>
|
</>
|
||||||
|
|
Reference in a new issue