Files
DownLink/backend/handlers/video.go
Arkaprabha Chakraborty 1a0330537a feat: caching & logging
2025-07-09 21:55:32 +05:30

110 lines
3.4 KiB
Go

package handlers
import (
"encoding/json"
"fmt"
"log/slog"
"net/http"
"strings"
"DownLink/models"
"DownLink/services"
"github.com/google/uuid"
)
type VideoHandler struct {
videoService *services.VideoService
}
func NewVideoHandler(videoService *services.VideoService) *VideoHandler {
return &VideoHandler{
videoService: videoService,
}
}
func (vh *VideoHandler) DownloadVideo(w http.ResponseWriter, r *http.Request) {
var req models.VideoDownloadRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
slog.Error("Failed to decode request body", "error", err)
vh.writeError(w, http.StatusBadRequest, "Invalid JSON")
return
}
if req.URL == "" || req.Quality == "" {
slog.Warn("Invalid request parameters", "url", req.URL, "quality", req.Quality)
vh.writeError(w, http.StatusBadRequest, "URL and Quality are required")
return
}
slog.Info("Starting video download", "url", req.URL, "quality", req.Quality)
outputPath, err := vh.videoService.DownloadVideo(req.URL, req.Quality)
if err != nil {
slog.Error("Video download failed", "url", req.URL, "quality", req.Quality, "error", err)
vh.writeError(w, http.StatusInternalServerError, err.Error())
return
}
// Determine if this was a cached response
isCached := !strings.Contains(outputPath, "dl_") && strings.Contains(outputPath, "cache")
// Only cleanup if it's a fresh download (not cached)
if strings.Contains(outputPath, "dl_") {
defer vh.videoService.CleanupTempDir(outputPath)
}
uid := uuid.New().String()
filename := fmt.Sprintf("video_%s.mp4", uid)
// Add cache status header
if isCached {
w.Header().Set("X-Cache-Status", "HIT")
slog.Info("Serving cached video", "url", req.URL, "quality", req.Quality, "file", outputPath)
} else {
w.Header().Set("X-Cache-Status", "MISS")
slog.Info("Serving fresh download", "url", req.URL, "quality", req.Quality, "file", outputPath)
}
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
w.Header().Set("Content-Type", "video/mp4")
http.ServeFile(w, r, outputPath)
}
func (vh *VideoHandler) HealthCheck(w http.ResponseWriter, r *http.Request) {
slog.Debug("Health check requested", "remote_addr", r.RemoteAddr)
w.WriteHeader(http.StatusOK)
w.Write([]byte("Backend for DownLink is running.\n"))
}
func (vh *VideoHandler) ClearCache(w http.ResponseWriter, r *http.Request) {
slog.Info("Cache clear requested")
if err := vh.videoService.CleanupExpiredCache(0); err != nil {
slog.Error("Failed to clear cache", "error", err)
vh.writeError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to clear cache: %v", err))
return
}
slog.Info("Cache cleared successfully")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"message": "Cache cleared successfully"})
}
func (vh *VideoHandler) GetCacheStatus(w http.ResponseWriter, r *http.Request) {
slog.Debug("Cache status requested")
status := vh.videoService.GetCacheStats()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(status)
}
func (vh *VideoHandler) writeError(w http.ResponseWriter, status int, message string) {
slog.Error("HTTP error response", "status", status, "message", message)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(models.ErrorResponse{Error: message})
}