428 lines
13 KiB
Go
428 lines
13 KiB
Go
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,
|
|
})
|
|
}
|