ghp / docs /architecture /directory-structure.md
QSLY's picture
deploy: build Hugging Face Space from source
00a912e
|
Raw
History Blame Contribute Delete
16.9 kB

目录结构

根目录

music-together/
├── packages/
│   ├── client/          # React 前端
│   ├── server/          # Node.js 后端
│   └── shared/          # 共享类型与常量
├── docs/                # 项目文档(含本文件 PROJECT_ARCHITECTURE.md)
├── package.json         # 根 package(工作区编排)
├── pnpm-workspace.yaml  # pnpm 工作区定义
├── pnpm-lock.yaml
├── README.md
└── .gitignore

packages/client/src/ — 前端源码

src/
├── main.tsx                    # 入口:ReactDOM.createRoot
├── App.tsx                     # 根组件:Router + Provider + ErrorBoundary + Suspense 懒加载
├── index.css                   # 全局样式:Tailwind + 配色变量 + 自定义动画
│
├── pages/                      # 页面级组件
│   ├── HomePage.tsx            #   大厅:创建/加入房间、房间列表
│   ├── RoomPage.tsx            #   房间:播放器 + 聊天(桌面侧栏/移动端 Drawer) + 覆盖层弹窗
│   └── NotFoundPage.tsx        #   404 页面
│
├── components/                 # UI 组件
│   ├── Chat/
│   │   ├── ChatMessage.tsx     #     单条消息(用户/系统)
│   │   └── ChatPanel.tsx       #     聊天面板(消息列表 + 输入框)
│   ├── Lobby/
│   │   ├── CreateRoomDialog.tsx #    创建房间弹窗
│   │   ├── NicknameDialog.tsx  #     设置昵称弹窗
│   │   ├── PasswordDialog.tsx  #     输入房间密码弹窗
│   │   ├── RoomCard.tsx        #     房间列表卡片
│   │   ├── UserPopover.tsx     #     用户信息气泡
│   │   ├── HeroSection.tsx     #     首页 Hero 标题区域
│   │   ├── ActionCards.tsx     #     创建/加入房间卡片
│   │   └── RoomListSection.tsx #     活跃房间列表区域
│   ├── TrackListItem.tsx        #   共享曲目行渲染(序号+封面+标题VIP+歌手可点击+时长+isAdded 添加按钮,memo 优化)
│   ├── VirtualTrackList.tsx     #   共享虚拟滚动曲目列表(@tanstack/react-virtual + 无限加载 + skeleton + 空态,forwardRef 暴露 scrollToTop)
│   ├── Overlays/
│   │   ├── QueueDrawer.tsx     #     播放队列抽屉(vaul Drawer,移动端底部/桌面端右侧)
│   │   ├── SearchDialog.tsx    #     音乐搜索弹窗(VirtualTrackList 虚拟滚动 + 自动无限加载 + AbortController 竞态防护)
│   │   ├── SettingsDialog.tsx  #     设置弹窗(壳,Tab 导航:房间/成员/账号/个人/外观,移动端 nav scrollbar-hide)
│   │   └── Settings/
│   │       ├── SettingRow.tsx              # 设置行共享组件
│   │       ├── RoomSettingsSection.tsx     # 房间设置(名称、密码)
│   │       ├── MembersSection.tsx          # 成员列表(角色管理)
│   │       ├── PlatformAuthSection.tsx     # 平台账号认证(VIP Cookie,旧版,已被 PlatformHub 替代)
│   │       ├── PlatformHub.tsx            # 平台中心(登录 + 歌单浏览 + 导入,替代 PlatformAuthSection)
│   │       ├── LoginSection.tsx           # 精简版平台登录区域(PlatformHub 子组件)
│   │       ├── PlaylistSection.tsx        # 歌单列表 + 手动输入(PlatformHub 子组件)
│   │       ├── PlaylistDetail.tsx         # 歌单详情(header + VirtualTrackList + queueKeys/addedIds 去重 + 动态全部添加过滤重复)
│   │       ├── ProfileSettingsSection.tsx  # 个人设置(昵称)
│   │       ├── AppearanceSection.tsx       # 外观设置(歌词 + 背景 + 布局)
│   │       ├── OtherSettingsSection.tsx    # 其他设置
│   │       ├── ManualCookieDialog.tsx      # 手动输入 Cookie 弹窗
│   │       └── QrLoginDialog.tsx           # 通用 QR 扫码登录弹窗(网易云 + 酷狗 + QQ 音乐)
│   ├── Player/
│   │   ├── constants.ts        #     共享动画常量(SPRING / LAYOUT_TRANSITION),NowPlaying 和 SongInfoBar 统一导入
│   │   ├── AudioPlayer.tsx     #     主播放器布局(桌面:左右分栏;移动:双模式封面/歌词切换)
│   │   ├── LyricDisplay.tsx    #     AMLL 歌词渲染(LRC 正则支持 [mm:ss] / [mm:ss.x] / [mm:ss.xx] / [mm:ss.xxx])
│   │   ├── NowPlaying.tsx      #     当前曲目展示(支持 compact 小封面横排模式 + layoutId 共享动画)
│   │   ├── SongInfoBar.tsx     #     歌曲信息栏(标题/艺术家 + 音量/聊天按钮,竖屏模式自适应缩放)
│   │   └── PlayerControls.tsx  #     进度条+播放控制+播放模式切换
│   ├── Room/
│   │   └── RoomHeader.tsx      #     房间头部(房间名/人数/连接状态;移动端 DropdownMenu 收纳设置/离开等操作)
│   ├── Vote/
│   │   └── VoteBanner.tsx      #     投票横幅(进行中的投票显示 + 投票按钮)
│   ├── InteractionGate.tsx     #   浏览器交互解锁(点击后才能播放音频)
│   └── ui/                     #   shadcn/ui 基础组件
│       ├── avatar.tsx
│       ├── badge.tsx
│       ├── button.tsx
│       ├── card.tsx
│       ├── dialog.tsx
│       ├── drawer.tsx
│       ├── dropdown-menu.tsx
│       ├── input.tsx
│       ├── label.tsx
│       ├── popover.tsx
│       ├── resize-handle.tsx
│       ├── scroll-area.tsx
│       ├── select.tsx
│       ├── separator.tsx
│       ├── marquee-text.tsx
│       ├── responsive-dialog.tsx
│       ├── sheet.tsx
│       ├── skeleton.tsx
│       ├── slider.tsx
│       ├── switch.tsx
│       ├── tabs.tsx
│       └── tooltip.tsx
│
├── hooks/                      # 自定义 Hooks
│   ├── useSocketEvent.ts       #   通用 Socket 事件订阅工具 Hook(自动 on/off,ref 稳定)
│   ├── usePlayer.ts            #   播放器主 hook(组合 useHowl + useLyric + usePlayerSync)
│   ├── useHowl.ts              #   Howler.js 音频实例管理
│   ├── useLyric.ts             #   歌词加载(TTML → 平台逐词 YRC/KRC → LRC)
│   ├── usePlayerSync.ts        #   播放同步(Scheduled Execution + conductor 上报 + 周期性漂移校正)
│   ├── useClockSync.ts         #   NTP 时钟同步 hook(校准客户端时钟与服务器对齐)
│   ├── useRoom.ts              #   房间组合 hook(编排 5 个子 hook,对外 API 不变)
│   ├── room/                   #   useRoom 子 hook(按职责拆分)
│   │   ├── useRoomState.ts     #     ROOM_STATE / JOIN / LEFT / SETTINGS / ROLE_CHANGED / ERROR + 挂载时补发 cookie(覆盖 HomePage 提前消费 ROOM_STATE 的场景)
│   │   ├── useChatSync.ts      #     CHAT_HISTORY / CHAT_MESSAGE
│   │   ├── useQueueSync.ts     #     QUEUE_UPDATED
│   │   ├── useAuthSync.ts      #     AUTH_SET_COOKIE_RESULT + localStorage 持久化(验证失败只做 toast 反馈,永不删除 cookie;删除权仅在 useAuth.logout)
│   │   └── useConnectionGuard.ts #   disconnect → resetAllRoomState
│   ├── useAuth.ts              #   平台认证 UI & Socket 事件
│   ├── useVote.ts              #   投票(使用 useSocketEvent)
│   ├── useChat.ts              #   聊天消息收发
│   ├── useLobby.ts             #   大厅房间列表与操作(使用 useSocketEvent)
│   ├── useQueue.ts             #   播放队列操作(含 addBatchTracks 批量添加)
│   ├── usePlaylist.ts          #   歌单管理(用户歌单列表、分页曲目获取 + 无限加载、URL 解析、批量导入)
│   ├── useIsMobile.ts          #   布局维度:orientation 检测(portrait=竖屏布局,landscape=横屏布局)
│   ├── useHasHover.ts          #   交互维度:hover 能力检测((hover: hover) 媒体查询,触控设备=false)
│   ├── useContainerPortrait.ts #   容器宽高比检测(ResizeObserver,用于播放器横竖屏切换)
│   └── useCoverWidth.ts        #   封面容器最小尺寸测量(约束信息栏/控件宽度与封面对齐)
│
├── stores/                     # Zustand 状态仓库
│   ├── playerStore.ts          #   播放状态(currentTrack, isPlaying, volume 等)
│   ├── roomStore.ts            #   房间状态(room, currentUser, users)
│   ├── chatStore.ts            #   聊天(messages, unreadCount, isChatOpen)
│   ├── lobbyStore.ts           #   大厅(rooms 列表, isLoading)
│   └── settingsStore.ts        #   设置(歌词参数、背景参数,持久化到 localStorage)
│
├── providers/                  # React Context Provider
│   ├── SocketProvider.tsx      #   Socket.IO 连接管理,提供 socket + isConnected + 断线/重连 Toast
│   └── AbilityProvider.tsx     #   CASL 权限上下文(基于 currentUser.role)
│
└── lib/                        # 工具库
    ├── config.ts               #   配置常量(SERVER_URL)
    ├── constants.ts            #   命名常量(定时器、阈值、布局尺寸)
    ├── clockSync.ts            #   NTP 时钟同步引擎(采样、offset 计算、getServerTime)
    ├── resetStores.ts          #   全局 store 重置工具
    ├── socket.ts               #   Socket.IO 客户端实例
    ├── storage.ts              #   localStorage 封装(带类型校验)
    ├── platform.ts             #   平台常量(PLATFORM_LABELS / PLATFORM_SHORT_LABELS / PLATFORM_COLORS / VIP_LABELS / 状态查找函数)
    ├── format.ts               #   格式化工具(时间、文本等)
    ├── audioUnlock.ts          #   浏览器音频自动播放解锁
    └── utils.ts                #   cn() + trackKey() 等通用工具

packages/server/src/ — 后端源码

src/
├── index.ts                    # 入口:Express + HTTP + Socket.IO 服务启动与优雅关闭
├── config.ts                   # 环境变量配置(PORT, CLIENT_URL, CORS)
│
├── controllers/                # 控制器:注册 Socket 事件处理器(薄编排层,不含业务逻辑)
│   ├── index.ts                #   统一注册入口
│   ├── roomController.ts       #   房间生命周期(创建/加入/离开/发现/设置/角色)
│   ├── playerController.ts     #   播放控制(play/pause/seek/next/prev/sync/set_mode)+ NTP ping/pong
│   ├── queueController.ts      #   队列管理(add/remove/reorder/clear)
│   ├── chatController.ts       #   聊天消息(含限流反馈)
│   ├── voteController.ts       #   投票系统(发起/投票/超时/执行,支持 set-mode / play-track / remove-track 投票)
│   ├── authController.ts       #   平台认证(QR 登录/Cookie 管理/状态查询;支持网易云/酷狗/QQ 音乐三平台;策略模式——通过 AUTH_PROVIDERS 映射表统一处理;fast path: 内存池命中跳过 API;slow path: getUserInfo + 任意失败重试 1 次)
│   └── playlistController.ts   #   歌单管理(获取用户歌单列表 via Socket,使用 getUserCookie 取请求者自己的 cookie,歌单私有)
│
├── services/                   # 服务层:业务逻辑
│   ├── roomService.ts          #   房间 CRUD + 角色管理 + 加入校验(validateJoinRequest)
│   ├── roomLifecycleService.ts #   房间生命周期定时器(空置删除)+ 防抖广播
│   ├── playerService.ts        #   播放状态管理 + 流 URL 解析 + 切歌防抖 + 加入播放同步
│   ├── queueService.ts         #   队列操作(reorder 保留未包含曲目防丢歌,getNextTrack 支持 4 种播放模式,clearQueue 清空,addBatchTracks 批量添加)
│   ├── chatService.ts          #   聊天消息处理 + HTML 转义(含系统消息)
│   ├── syncService.ts          #   播放位置估算工具(estimateCurrentTime)
│   ├── musicProvider.ts        #   音乐数据聚合(3 层引用式 LRU 缓存 + 外部 API 超时保护 + 歌单分页获取;Netease 歌单使用 ncmApi.playlist_track_all 分块请求突破 1000 首限制,Kugou 用户歌单使用原生 API (get_other_list_file_nofilt) + Meting fallback,Tencent 使用 Meting 原始模式保留 VIP/时长字段)
│   ├── authService.ts          #   Cookie 池管理(房间级作用域;getAnyCookie 用于 VIP 播放共享,getUserCookie 用于歌单等用户私有操作)
│   ├── authProvider.ts         #   统一认证接口(AuthProvider 接口定义 + GetUserInfoResult/UserInfoData 共享类型 + AUTH_PROVIDERS 策略映射表)
│   ├── neteaseAuthService.ts   #   网易云 API 认证(QR / Cookie 验证 / 用户信息 / 用户歌单列表;getUserInfo 返回 { ok, data? } | { ok: false, reason: 'expired' | 'error' } 区分过期与临时故障)
│   ├── kugouAuthService.ts    #   酷狗 API 认证(QR 扫码登录 + VIP 检查 + 用户昵称(RSA) + 用户歌单列表 + 歌单歌曲获取;kugouRequest 含 HTTP 状态检查与 JSON 安全解析;自包含签名实现,状态码归一化为 800-803 与网易云统一)
│   ├── tencentAuthService.ts  #   QQ 音乐认证(5 步 OAuth QR 扫码登录:ptqrshow/ptqrlogin/check_sig/authorize/QQLogin 换取 musickey;zzc 签名防风控;getUserInfo 获取昵称 + VIP 状态;getUserPlaylists 获取自建 + 收藏歌单;getPlaylistTracks 分页获取歌单歌曲)
│   └── voteService.ts          #   投票状态管理
│
├── repositories/               # 数据仓库:内存状态与 SQLite 持久化
│   ├── types.ts                #   接口定义(RoomRepository, ChatRepository)
│   ├── roomRepository.ts       #   房间数据 + Socket 映射 + per-socket RTT + roomToSockets 反向索引(Map<string, RoomData>)
│   ├── chatRepository.ts       #   聊天记录(内存最多 200 条;永久房间同步持久化到 SQLite)
│   └── audioProxyPolicyRepository.ts # 全局酷狗音频代理策略(SQLite server_settings)
│
├── middleware/                  # Socket.IO 中间件
│   ├── types.ts                #   TypedServer, TypedSocket, HandlerContext
│   ├── withRoom.ts             #   房间成员身份校验
│   ├── withControl.ts          #   操作权限校验(包装 withRoom)
│   └── socketRateLimiter.ts    #   Socket 事件速率限制(per-socket,10次/5秒)+ 断连清理(cleanupSocketRateLimit)
│
├── routes/                     # Express REST 路由
│   ├── music.ts                #   GET /api/music/search|url|lyric|cover|playlist|ttml(统一 validated() 路由包装器消除重复 try/catch + Zod 模式)
│   └── rooms.ts                #   GET /api/rooms/:roomId/check(房间预检)
│
├── types/
│   └── meting.d.ts             #   @meting/core 类型声明
│
└── utils/
    ├── logger.ts               #   结构化日志(基于 pino,info/warn/error + JSON context)
    └── roomUtils.ts            #   房间数据转换纯函数(toPublicRoomState / toPublicRoomStateForOwner,密码仅 owner 可见)

packages/shared/src/ — 共享代码

src/
├── index.ts           # 统一导出(re-export 所有模块)
├── types.ts           # 核心类型:ERROR_CODE, Track, RoomState, PlayState, ScheduledPlayState, PlayMode, AudioQuality, User, ChatMessage, VoteAction (incl. play-track, remove-track), VoteState, RoomListItem, Playlist
├── events.ts          # 事件常量:EVENTS 对象(room:*, player:*, queue:*, chat:*, auth:*, ntp:*, playlist:*)
├── socket-types.ts    # Socket.IO 类型:ServerToClientEvents, ClientToServerEvents
├── constants.ts       # 业务常量:LIMITS(长度/数量限制), TIMING(同步间隔/宽限期), NTP(时钟同步参数), QR_STATUS(扫码状态码), QR_TIMING(轮询间隔)
├── schemas.ts         # Zod 验证 schema
└── abilities.ts       # CASL 权限定义(Actions incl. set-mode, Subjects, defineAbilityFor)