package handlers import ( "Yimaru-Backend/internal/domain" "Yimaru-Backend/internal/pkgs/vimeo" "fmt" "strconv" "github.com/gofiber/fiber/v2" ) type CreatePullUploadRequest struct { Name string `json:"name" validate:"required"` Description string `json:"description"` SourceURL string `json:"source_url" validate:"required,url"` FileSize int64 `json:"file_size" validate:"required,gt=0"` } type CreateTusUploadRequest struct { Name string `json:"name" validate:"required"` Description string `json:"description"` FileSize int64 `json:"file_size" validate:"required,gt=0"` } type GetEmbedCodeRequest struct { VideoID string `json:"video_id" validate:"required"` Width int `json:"width"` Height int `json:"height"` Autoplay bool `json:"autoplay"` Loop bool `json:"loop"` Muted bool `json:"muted"` Background bool `json:"background"` } type VimeoVideoResponse struct { VimeoID string `json:"vimeo_id"` URI string `json:"uri"` Name string `json:"name"` Description string `json:"description"` Duration int `json:"duration"` Width int `json:"width"` Height int `json:"height"` Link string `json:"link"` EmbedURL string `json:"embed_url"` EmbedHTML string `json:"embed_html"` ThumbnailURL string `json:"thumbnail_url"` Status string `json:"status"` TranscodeStatus string `json:"transcode_status"` } type VimeoUploadResponse struct { VimeoID string `json:"vimeo_id"` URI string `json:"uri"` Link string `json:"link"` UploadLink string `json:"upload_link,omitempty"` Status string `json:"status"` } type VimeoEmbedResponse struct { VideoID string `json:"video_id"` EmbedURL string `json:"embed_url"` EmbedHTML string `json:"embed_html"` } // GetVimeoVideo godoc // @Summary Get video information from Vimeo // @Description Retrieves video details from Vimeo by video ID // @Tags Vimeo // @Accept json // @Produce json // @Param video_id path string true "Vimeo Video ID" // @Success 200 {object} VimeoVideoResponse // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/vimeo/videos/{video_id} [get] func (h *Handler) GetVimeoVideo(c *fiber.Ctx) error { if h.vimeoSvc == nil { return c.Status(fiber.StatusServiceUnavailable).JSON(domain.ErrorResponse{ Message: "Vimeo service is not configured", Error: "Vimeo service is not enabled or missing access token", }) } videoID := c.Params("video_id") if videoID == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "video_id is required", Error: "video_id path parameter is empty", }) } info, err := h.vimeoSvc.GetVideoInfo(c.Context(), videoID) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to get video info", Error: err.Error(), }) } return c.JSON(domain.Response{ Message: "Video retrieved successfully", Data: VimeoVideoResponse{ VimeoID: info.VimeoID, URI: info.URI, Name: info.Name, Description: info.Description, Duration: info.Duration, Width: info.Width, Height: info.Height, Link: info.Link, EmbedURL: info.EmbedURL, EmbedHTML: info.EmbedHTML, ThumbnailURL: info.ThumbnailURL, Status: info.Status, TranscodeStatus: info.TranscodeStatus, }, Success: true, StatusCode: fiber.StatusOK, }) } // CreatePullUpload godoc // @Summary Create a pull upload to Vimeo // @Description Initiates a pull upload where Vimeo fetches the video from a URL // @Tags Vimeo // @Accept json // @Produce json // @Param request body CreatePullUploadRequest true "Pull Upload Request" // @Success 201 {object} VimeoUploadResponse // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/vimeo/uploads/pull [post] func (h *Handler) CreatePullUpload(c *fiber.Ctx) error { if h.vimeoSvc == nil { return c.Status(fiber.StatusServiceUnavailable).JSON(domain.ErrorResponse{ Message: "Vimeo service is not configured", Error: "Vimeo service is not configured", }) } var req CreatePullUploadRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid request body", Error: err.Error(), }) } if valErrs, ok := h.validator.Validate(c, req); !ok { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to validate request body", Error: fmt.Sprintf("%v", valErrs), }) } result, err := h.vimeoSvc.CreatePullUpload(c.Context(), req.Name, req.Description, req.SourceURL, req.FileSize) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to create Vimeo upload", Error: err.Error(), }) } return c.Status(fiber.StatusCreated).JSON(domain.Response{ Message: "Vimeo upload created successfully", Data: result, Success: true, StatusCode: fiber.StatusCreated, }) } // CreateTusUpload godoc // @Summary Create a TUS resumable upload to Vimeo // @Description Initiates a TUS resumable upload and returns the upload link // @Tags Vimeo // @Accept json // @Produce json // @Param request body CreateTusUploadRequest true "TUS Upload Request" // @Success 201 {object} VimeoUploadResponse // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/vimeo/uploads/tus [post] func (h *Handler) CreateTusUpload(c *fiber.Ctx) error { if h.vimeoSvc == nil { return c.Status(fiber.StatusServiceUnavailable).JSON(domain.ErrorResponse{ Message: "Vimeo service is not configured", Error: "Vimeo service is not enabled or missing access token", }) } var req CreateTusUploadRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid request body", Error: err.Error(), }) } if valErrs, ok := h.validator.Validate(c, req); !ok { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to validate request body", Error: fmt.Sprintf("%v", valErrs), }) } result, err := h.vimeoSvc.CreateTusUpload(c.Context(), req.Name, req.Description, req.FileSize) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to create Vimeo TUS upload", Error: err.Error(), }) } return c.Status(fiber.StatusCreated).JSON(domain.Response{ Message: "Vimeo TUS upload created successfully", Data: result, Success: true, StatusCode: fiber.StatusCreated, }) } // GetEmbedCode godoc // @Summary Get embed code for a Vimeo video // @Description Generates an embeddable player iframe for the video // @Tags Vimeo // @Accept json // @Produce json // @Param video_id path string true "Vimeo Video ID" // @Param width query int false "Player width" default(640) // @Param height query int false "Player height" default(360) // @Param autoplay query bool false "Autoplay video" // @Param loop query bool false "Loop video" // @Param muted query bool false "Mute video" // @Param background query bool false "Background mode (no controls)" // @Success 200 {object} VimeoEmbedResponse // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/vimeo/videos/{video_id}/embed [get] func (h *Handler) GetEmbedCode(c *fiber.Ctx) error { if h.vimeoSvc == nil { return c.Status(fiber.StatusServiceUnavailable).JSON(domain.ErrorResponse{ Message: "Vimeo service is not configured", Error: "Vimeo service is not configured", }) } videoID := c.Params("video_id") if videoID == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "video_id is required", Error: "video_id is empty", }) } width, _ := strconv.Atoi(c.Query("width", "640")) height, _ := strconv.Atoi(c.Query("height", "360")) autoplay := c.Query("autoplay") == "true" loop := c.Query("loop") == "true" muted := c.Query("muted") == "true" background := c.Query("background") == "true" opts := &vimeo.EmbedOptions{ Autoplay: autoplay, Loop: loop, Muted: muted, Background: background, Title: true, Byline: true, Portrait: true, } embedHTML, err := h.vimeoSvc.GetEmbedCode(c.Context(), videoID, width, height, opts) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to get embed code", Error: err.Error(), }) } embedURL := h.vimeoSvc.GeneratePlayerURL(videoID, opts) return c.JSON(domain.Response{ Message: "Embed code retrieved successfully", Data: VimeoEmbedResponse{ VideoID: videoID, EmbedURL: embedURL, EmbedHTML: embedHTML, }, Success: true, StatusCode: fiber.StatusOK, }) } // DeleteVimeoVideo godoc // @Summary Delete a video from Vimeo // @Description Deletes a video from the Vimeo account // @Tags Vimeo // @Accept json // @Produce json // @Param video_id path string true "Vimeo Video ID" // @Success 204 // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/vimeo/videos/{video_id} [delete] func (h *Handler) DeleteVimeoVideo(c *fiber.Ctx) error { if h.vimeoSvc == nil { return c.Status(fiber.StatusServiceUnavailable).JSON(domain.ErrorResponse{ Message: "Vimeo service is not configured", Error: "Vimeo service is not enabled or missing access token", }) } videoID := c.Params("video_id") if videoID == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "video_id is required", Error: "video_id path parameter is empty", }) } if err := h.vimeoSvc.DeleteVideo(c.Context(), videoID); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to delete video", Error: err.Error(), }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Video deleted successfully", Success: true, StatusCode: fiber.StatusOK, }) } // GetTranscodeStatus godoc // @Summary Get transcode status of a Vimeo video // @Description Returns the current transcoding status of a video // @Tags Vimeo // @Accept json // @Produce json // @Param video_id path string true "Vimeo Video ID" // @Success 200 {object} map[string]string // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/vimeo/videos/{video_id}/status [get] func (h *Handler) GetTranscodeStatus(c *fiber.Ctx) error { if h.vimeoSvc == nil { return c.Status(fiber.StatusServiceUnavailable).JSON(domain.ErrorResponse{ Message: "Vimeo service is not configured", Error: "Vimeo service is not enabled or missing access token", }) } videoID := c.Params("video_id") if videoID == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "video_id is required", Error: "video_id path parameter is empty", }) } status, err := h.vimeoSvc.GetTranscodeStatus(c.Context(), videoID) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to get transcode status", Error: err.Error(), }) } return c.JSON(domain.Response{ Message: "Transcode status retrieved successfully", Data: fiber.Map{ "video_id": videoID, "status": status, }, Success: true, StatusCode: fiber.StatusOK, }) } // GetOEmbed godoc // @Summary Get oEmbed data for a Vimeo URL // @Description Fetches oEmbed metadata for a Vimeo video URL // @Tags Vimeo // @Accept json // @Produce json // @Param url query string true "Vimeo video URL" // @Param width query int false "Desired width" // @Param height query int false "Desired height" // @Success 200 {object} vimeo.OEmbedResponse // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/vimeo/oembed [get] func (h *Handler) GetOEmbed(c *fiber.Ctx) error { if h.vimeoSvc == nil { return c.Status(fiber.StatusServiceUnavailable).JSON(domain.ErrorResponse{ Message: "Vimeo service is not configured", Error: "Vimeo service is not enabled or missing access token", }) } vimeoURL := c.Query("url") if vimeoURL == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "url query parameter is required", Error: "url query parameter is empty", }) } width, _ := strconv.Atoi(c.Query("width", "0")) height, _ := strconv.Atoi(c.Query("height", "0")) oembed, err := h.vimeoSvc.GetOEmbed(c.Context(), vimeoURL, width, height) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to get oEmbed data", Error: err.Error(), }) } return c.JSON(domain.Response{ Message: "oEmbed data retrieved successfully", Data: oembed, Success: true, StatusCode: fiber.StatusOK, }) }