139 lines
4.1 KiB
Go
139 lines
4.1 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
|
|
"veza-backend-api/internal/models"
|
|
)
|
|
|
|
// QueueService handles user playback queue operations
|
|
type QueueService struct {
|
|
db *gorm.DB
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewQueueService creates a new QueueService
|
|
func NewQueueService(db *gorm.DB, logger *zap.Logger) *QueueService {
|
|
if logger == nil {
|
|
logger = zap.NewNop()
|
|
}
|
|
return &QueueService{db: db, logger: logger}
|
|
}
|
|
|
|
// GetOrCreateQueue returns the user's queue, creating it if it doesn't exist
|
|
func (s *QueueService) GetOrCreateQueue(ctx context.Context, userID uuid.UUID) (*models.Queue, error) {
|
|
var q models.Queue
|
|
err := s.db.WithContext(ctx).Where("user_id = ?", userID).First(&q).Error
|
|
if err == nil {
|
|
return s.loadQueueWithItems(ctx, &q)
|
|
}
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, err
|
|
}
|
|
q = models.Queue{UserID: userID}
|
|
if err := s.db.WithContext(ctx).Create(&q).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return s.loadQueueWithItems(ctx, &q)
|
|
}
|
|
|
|
func (s *QueueService) loadQueueWithItems(ctx context.Context, q *models.Queue) (*models.Queue, error) {
|
|
var items []models.QueueItem
|
|
if err := s.db.WithContext(ctx).Where("queue_id = ?", q.ID).Order("position ASC").Preload("Track").Find(&items).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
q.Items = items
|
|
return q, nil
|
|
}
|
|
|
|
// UpdateQueue updates queue metadata (order, current position, etc.)
|
|
func (s *QueueService) UpdateQueue(ctx context.Context, userID uuid.UUID, req *UpdateQueueRequest) (*models.Queue, error) {
|
|
q, err := s.GetOrCreateQueue(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if req.CurrentTrackID != nil {
|
|
q.CurrentTrackID = req.CurrentTrackID
|
|
}
|
|
if req.CurrentPosition != nil {
|
|
q.CurrentPosition = *req.CurrentPosition
|
|
}
|
|
if req.IsPlaying != nil {
|
|
q.IsPlaying = *req.IsPlaying
|
|
}
|
|
if req.Shuffle != nil {
|
|
q.Shuffle = *req.Shuffle
|
|
}
|
|
if req.RepeatMode != nil {
|
|
q.RepeatMode = *req.RepeatMode
|
|
}
|
|
if req.Volume != nil {
|
|
q.Volume = *req.Volume
|
|
}
|
|
if req.ItemOrder != nil {
|
|
for i, itemID := range req.ItemOrder {
|
|
s.db.WithContext(ctx).Model(&models.QueueItem{}).Where("id = ? AND queue_id = ?", itemID, q.ID).Update("position", i)
|
|
}
|
|
}
|
|
if err := s.db.WithContext(ctx).Save(q).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return s.loadQueueWithItems(ctx, q)
|
|
}
|
|
|
|
// UpdateQueueRequest represents the request body for updating a queue
|
|
type UpdateQueueRequest struct {
|
|
CurrentTrackID *uuid.UUID `json:"current_track_id"`
|
|
CurrentPosition *int `json:"current_position"`
|
|
IsPlaying *bool `json:"is_playing"`
|
|
Shuffle *bool `json:"shuffle"`
|
|
RepeatMode *string `json:"repeat_mode"`
|
|
Volume *int `json:"volume"`
|
|
ItemOrder []uuid.UUID `json:"item_order"`
|
|
}
|
|
|
|
// AddToQueue adds a track to the user's queue
|
|
func (s *QueueService) AddToQueue(ctx context.Context, userID uuid.UUID, trackID uuid.UUID) (*models.QueueItem, error) {
|
|
q, err := s.GetOrCreateQueue(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var maxPos int
|
|
s.db.WithContext(ctx).Model(&models.QueueItem{}).Where("queue_id = ?", q.ID).Select("COALESCE(MAX(position), -1)").Scan(&maxPos)
|
|
item := models.QueueItem{QueueID: q.ID, TrackID: trackID, Position: maxPos + 1}
|
|
if err := s.db.WithContext(ctx).Create(&item).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
s.db.WithContext(ctx).Preload("Track").First(&item, item.ID)
|
|
return &item, nil
|
|
}
|
|
|
|
// RemoveFromQueue removes an item from the queue
|
|
func (s *QueueService) RemoveFromQueue(ctx context.Context, userID uuid.UUID, itemID uuid.UUID) error {
|
|
q, err := s.GetOrCreateQueue(ctx, userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result := s.db.WithContext(ctx).Where("id = ? AND queue_id = ?", itemID, q.ID).Delete(&models.QueueItem{})
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
if result.RowsAffected == 0 {
|
|
return gorm.ErrRecordNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ClearQueue removes all items from the user's queue
|
|
func (s *QueueService) ClearQueue(ctx context.Context, userID uuid.UUID) error {
|
|
q, err := s.GetOrCreateQueue(ctx, userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return s.db.WithContext(ctx).Where("queue_id = ?", q.ID).Delete(&models.QueueItem{}).Error
|
|
}
|