package video import ( "fmt" "net/http" "strconv" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" "stream.api/internal/config" "stream.api/internal/database/model" "stream.api/internal/database/query" "stream.api/pkg/logger" "stream.api/pkg/response" "stream.api/pkg/storage" ) type Handler struct { logger logger.Logger cfg *config.Config storage storage.Provider } func NewHandler(l logger.Logger, cfg *config.Config, s storage.Provider) VideoHandler { return &Handler{ logger: l, cfg: cfg, storage: s, } } // @Summary Get Upload URL // @Description Generate presigned URL for video upload // @Tags video // @Accept json // @Produce json // @Param request body UploadURLRequest true "File Info" // @Success 200 {object} response.Response // @Failure 400 {object} response.Response // @Failure 500 {object} response.Response // @Router /videos/upload-url [post] // @Security BearerAuth func (h *Handler) GetUploadURL(c *gin.Context) { var req UploadURLRequest if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, http.StatusBadRequest, err.Error()) return } userID := c.GetString("userID") fileID := uuid.New().String() key := fmt.Sprintf("videos/%s/%s-%s", userID, fileID, req.Filename) url, err := h.storage.GeneratePresignedURL(key, 15*time.Minute) if err != nil { h.logger.Error("Failed to generate presigned URL", "error", err) response.Error(c, http.StatusInternalServerError, "Storage error") return } response.Success(c, gin.H{ "upload_url": url, "key": key, "file_id": fileID, // Temporary ID, actual video record ID might differ or be same }) } // @Summary Create Video // @Description Create video record after upload // @Tags video // @Accept json // @Produce json // @Param request body CreateVideoRequest true "Video Info" // @Success 201 {object} response.Response{data=model.Video} // @Failure 400 {object} response.Response // @Failure 500 {object} response.Response // @Router /videos [post] // @Security BearerAuth func (h *Handler) CreateVideo(c *gin.Context) { var req CreateVideoRequest if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, http.StatusBadRequest, err.Error()) return } userID := c.GetString("userID") video := &model.Video{ ID: uuid.New().String(), UserID: userID, Title: req.Title, Description: req.Description, URL: req.URL, Size: req.Size, Duration: req.Duration, Format: req.Format, Status: "PUBLIC", StorageType: "S3", } v := query.Video if err := v.WithContext(c.Request.Context()).Create(video); err != nil { h.logger.Error("Failed to create video record", "error", err) response.Error(c, http.StatusInternalServerError, "Failed to create video") return } response.Created(c, gin.H{"video": video}) } // @Summary List Videos // @Description Get paginated videos // @Tags video // @Produce json // @Param page query int false "Page number" default(1) // @Param limit query int false "Page size" default(10) // @Success 200 {object} response.Response // @Failure 500 {object} response.Response // @Router /videos [get] // @Security BearerAuth func (h *Handler) ListVideos(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10")) offset := (page - 1) * limit v := query.Video videos, count, err := v.WithContext(c.Request.Context()). Where(v.Status.Eq("PUBLIC")). Order(v.CreatedAt.Desc()). FindByPage(offset, limit) if err != nil { h.logger.Error("Failed to fetch videos", "error", err) response.Error(c, http.StatusInternalServerError, "Failed to fetch videos") return } response.Success(c, gin.H{ "videos": videos, "total": count, "page": page, "limit": limit, }) } // @Summary Get Video // @Description Get video details by ID // @Tags video // @Produce json // @Param id path string true "Video ID" // @Success 200 {object} response.Response{data=model.Video} // @Failure 404 {object} response.Response // @Router /videos/{id} [get] // @Security BearerAuth func (h *Handler) GetVideo(c *gin.Context) { id := c.Param("id") v := query.Video video, err := v.WithContext(c.Request.Context()).Where(v.ID.Eq(id)).First() if err != nil { response.Error(c, http.StatusNotFound, "Video not found") return } response.Success(c, gin.H{"video": video}) }