package room import ( "context" "database/sql" "fmt" "time" ) // PlaylistItem 表示房间共享播放列表中的一个媒体项。 type PlaylistItem struct { ID int64 `json:"id"` RoomName string `json:"room_name"` URL string `json:"url"` Title string `json:"title"` AddedBy string `json:"added_by"` SortOrder int32 `json:"sort_order"` CreatedAt time.Time `json:"created_at"` } // PlaybackState 表示房间当前的同步播放状态。 type PlaybackState struct { Room string `json:"room"` Position float64 `json:"position"` Playing bool `json:"playing"` Rate float64 `json:"rate"` CurrentItem int64 `json:"current_item_id"` UpdatedAt int64 `json:"updated_at"` UpdatedBy string `json:"updated_by"` } func InitPlaylistSchema(db *sql.DB) error { stmts := []string{ `CREATE TABLE IF NOT EXISTS playlist_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, room TEXT NOT NULL, url TEXT NOT NULL, title TEXT NOT NULL DEFAULT '', added_by TEXT NOT NULL, sort_order INTEGER NOT NULL DEFAULT 0, created_at INTEGER NOT NULL, FOREIGN KEY (room) REFERENCES rooms_v2(name) ON DELETE CASCADE )`, `CREATE INDEX IF NOT EXISTS idx_playlist_room ON playlist_items(room, sort_order)`, `CREATE TABLE IF NOT EXISTS playback_state ( room TEXT PRIMARY KEY, position REAL NOT NULL DEFAULT 0, playing INTEGER NOT NULL DEFAULT 0, rate REAL NOT NULL DEFAULT 1, current_item_id INTEGER NOT NULL DEFAULT 0, updated_at INTEGER NOT NULL, updated_by TEXT NOT NULL DEFAULT '', FOREIGN KEY (room) REFERENCES rooms_v2(name) ON DELETE CASCADE )`, } for _, stmt := range stmts { if _, err := db.Exec(stmt); err != nil { return fmt.Errorf("init playlist schema: %w", err) } } return nil } // AddPlaylistItem 向房间播放列表添加一个媒体项。 func (s *Store) AddPlaylistItem(ctx context.Context, item *PlaylistItem) (*PlaylistItem, error) { if s.db == nil { return nil, fmt.Errorf("no db") } item.CreatedAt = time.Now() res, err := s.db.ExecContext(ctx, `INSERT INTO playlist_items (room, url, title, added_by, sort_order, created_at) VALUES (?, ?, ?, ?, ?, ?)`, item.RoomName, item.URL, item.Title, item.AddedBy, item.SortOrder, item.CreatedAt.Unix()) if err != nil { return nil, err } id, _ := res.LastInsertId() item.ID = id return item, nil } // ListPlaylistItems 返回房间的播放列表(按排序字段排列)。 func (s *Store) ListPlaylistItems(ctx context.Context, roomName string) ([]*PlaylistItem, error) { if s.db == nil { return nil, nil } rows, err := s.db.QueryContext(ctx, `SELECT id, room, url, title, added_by, sort_order, created_at FROM playlist_items WHERE room = ? ORDER BY sort_order ASC, id ASC`, roomName) if err != nil { return nil, err } defer rows.Close() var out []*PlaylistItem for rows.Next() { var it PlaylistItem var createdAt int64 if err := rows.Scan(&it.ID, &it.RoomName, &it.URL, &it.Title, &it.AddedBy, &it.SortOrder, &createdAt); err != nil { return nil, err } it.CreatedAt = time.Unix(createdAt, 0) out = append(out, &it) } return out, rows.Err() } // RemovePlaylistItem 删除播放列表中的一个媒体项。 func (s *Store) RemovePlaylistItem(ctx context.Context, roomName string, itemID int64) error { if s.db == nil { return fmt.Errorf("no db") } _, err := s.db.ExecContext(ctx, `DELETE FROM playlist_items WHERE room = ? AND id = ?`, roomName, itemID) return err } // ReorderPlaylistItems 批量更新播放列表项的排序。 func (s *Store) ReorderPlaylistItems(ctx context.Context, roomName string, ids []int64) error { if s.db == nil { return fmt.Errorf("no db") } for i, id := range ids { if _, err := s.db.ExecContext(ctx, `UPDATE playlist_items SET sort_order = ? WHERE room = ? AND id = ?`, i, roomName, id); err != nil { return err } } return nil } // SavePlaybackState 保存或更新房间的同步播放状态。 func (s *Store) SavePlaybackState(ctx context.Context, ps *PlaybackState) error { if s.db == nil { return fmt.Errorf("no db") } ps.UpdatedAt = time.Now().Unix() _, err := s.db.ExecContext(ctx, `INSERT INTO playback_state (room, position, playing, rate, current_item_id, updated_at, updated_by) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(room) DO UPDATE SET position=excluded.position, playing=excluded.playing, rate=excluded.rate, current_item_id=excluded.current_item_id, updated_at=excluded.updated_at, updated_by=excluded.updated_by`, ps.Room, ps.Position, boolToInt(ps.Playing), ps.Rate, ps.CurrentItem, ps.UpdatedAt, ps.UpdatedBy) return err } // GetPlaybackState 获取房间的同步播放状态。 func (s *Store) GetPlaybackState(ctx context.Context, roomName string) (*PlaybackState, error) { if s.db == nil { return nil, sql.ErrNoRows } var ps PlaybackState var playing int err := s.db.QueryRowContext(ctx, `SELECT room, position, playing, rate, current_item_id, updated_at, updated_by FROM playback_state WHERE room = ?`, roomName). Scan(&ps.Room, &ps.Position, &playing, &ps.Rate, &ps.CurrentItem, &ps.UpdatedAt, &ps.UpdatedBy) if err != nil { return nil, err } ps.Playing = playing != 0 return &ps, nil }