Skip to content

Commit

Permalink
player: 更新了搜索功能,歌曲卡片暂无可交互功能,为播放列表提供了其他信息
Browse files Browse the repository at this point in the history
ws-protocol: 增加了直接传递 TTML 歌词的报文
  • Loading branch information
Steve-xmh committed Nov 1, 2024
1 parent a0a9f13 commit 92953de
Show file tree
Hide file tree
Showing 17 changed files with 822 additions and 334 deletions.
1 change: 1 addition & 0 deletions packages/player/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@applemusic-like-lyrics/react-full": "workspace:^",
"@applemusic-like-lyrics/ws-protocol": "workspace:^",
"@dnd-kit/core": "^6.1.0",
"@iconify/icons-fluent": "^1.2.38",
"@iconify/icons-ic": "^1.2.13",
"@iconify/react": "^5.0.2",
"@monaco-editor/react": "^4.6.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
grid-column: 3 / 4;
min-width: 0;
min-height: 0;
overflow: hidden;
overflow: hidden auto;
}

.playbar {
Expand Down
4 changes: 2 additions & 2 deletions packages/player/src/components/NowPlayingBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
hideNowPlayingBarAtom,
playlistCardOpenedAtom,
} from "../../states/index.ts";
import { PlaylistCard } from "../PlaylistCard/index.tsx";
import { NowPlaylistCard } from "../NowPlaylistCard/index.tsx";
import styles from "./index.module.css";

export const NowPlayingBar: FC = () => {
Expand Down Expand Up @@ -88,7 +88,7 @@ export const NowPlayingBar: FC = () => {
right="0"
bottom="calc(var(--amll-player-playbar-bottom) + var(--space-3))"
>
<PlaylistCard className={classNames(styles.playlistCard)} />
<NowPlaylistCard className={classNames(styles.playlistCard)} />
</Flex>
)}
<Flex
Expand Down
151 changes: 151 additions & 0 deletions packages/player/src/components/NowPlaylistCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { PlayIcon } from "@radix-ui/react-icons";
import { Avatar, Box, Flex, type FlexProps, Inset } from "@radix-ui/themes";
import classNames from "classnames";
import { useLiveQuery } from "dexie-react-hooks";
import { useAtomValue } from "jotai";
import md5 from "md5";
import {
type FC,
type HTMLProps,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
} from "react";
import { Trans } from "react-i18next";
import { ViewportList, type ViewportListRef } from "react-viewport-list";
import { type Song, db } from "../../dexie.ts";
import {
currentPlaylistAtom,
currentPlaylistMusicIndexAtom,
} from "../../states/index.ts";
import { type SongData, emitAudioThread } from "../../utils/player.ts";
import styles from "./index.module.css";

// TODO: 会产生闪烁更新,需要检查修正
const PlaylistSongItem: FC<
{
songData: SongData;
index: number;
} & HTMLProps<HTMLDivElement>
> = ({ songData, className, index, ...props }) => {
const playlistIndex = useAtomValue(currentPlaylistMusicIndexAtom);
const [songId, setSongId] = useState<string>("");
const lastSongId = useRef("");

useLayoutEffect(() => {
if (songData.type === "local") {
const newSongId = md5(songData.filePath);
if (lastSongId.current !== newSongId) {
console.log("newSongId", lastSongId.current, "->", newSongId);
setSongId(newSongId);
}
lastSongId.current = newSongId;
}
}, [songData]);

const [curSongInfo, setCurSongInfo] = useState<Song>();
const songInfo = useLiveQuery(() => db.songs.get(songId), [songId]);

useLayoutEffect(() => {
if (songInfo) setCurSongInfo(songInfo);
}, [songInfo]);

const name = useMemo(() => {
if (curSongInfo?.songName) return curSongInfo?.songName;
if (songData.type === "local") return songData.filePath;
return "";
}, [songData, curSongInfo]);

const artists = useMemo(() => {
if (curSongInfo) return curSongInfo?.songArtists ?? "";
return "";
}, [curSongInfo]);

const [cover, setCover] = useState("");

useLayoutEffect(() => {
if (curSongInfo?.cover) {
const newUri = URL.createObjectURL(curSongInfo.cover);
setCover(newUri);
return () => {
URL.revokeObjectURL(newUri);
};
}
}, [curSongInfo]);

return (
<div
className={classNames(className, styles.playlistSongItem)}
onDoubleClick={() => {
emitAudioThread("jumpToSong", {
songIndex: index,
});
}}
{...props}
>
<Avatar size="4" fallback={<div />} src={cover} />
<div className={styles.musicInfo}>
<div className={styles.name}>{name}</div>
<div className={styles.artists}>{artists}</div>
</div>
{playlistIndex === index && <PlayIcon />}
</div>
);
};

export const NowPlaylistCard: FC<FlexProps> = (props) => {
const playlist = useAtomValue(currentPlaylistAtom);
const playlistIndex = useAtomValue(currentPlaylistMusicIndexAtom);
const playlistRef = useRef<ViewportListRef>();
const playlistContainerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (playlistRef.current) {
playlistRef.current.scrollToIndex({
index: playlistIndex,
});
}
}, [playlistIndex]);

return (
<Flex
direction="column"
maxWidth="400px"
maxHeight="500px"
style={{
height: "50vh",
width: "max(10vw, 50vh)",
backdropFilter: "blur(1em)",
backgroundColor: "var(--black-a8)",
}}
{...props}
>
<Box py="3" px="4">
<Trans i18nKey="playbar.playlist.title">当前播放列表</Trans>
</Box>
<Inset
clip="padding-box"
side="bottom"
pb="current"
style={{ overflowY: "auto" }}
ref={playlistContainerRef}
>
<ViewportList
items={playlist}
ref={playlistRef}
viewportRef={playlistContainerRef}
>
{(songData, index) => (
<PlaylistSongItem
key={`playlist-song-item-${index}`}
songData={songData}
index={index}
/>
)}
</ViewportList>
</Inset>
</Flex>
);
};
Loading

0 comments on commit 92953de

Please sign in to comment.