downloads.go
3324 bytes
1package handler
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "path/filepath"
8 "regexp"
9 "strings"
10)
11
12func sanitizeFilename(title string) string {
13 title = strings.ReplaceAll(title, " ", "_")
14
15 reg := regexp.MustCompile(`[^a-zA-Z0-9_\-\.]`)
16 title = reg.ReplaceAllString(title, "")
17
18 if len(title) > 200 {
19 title = title[:200]
20 }
21
22 return title
23}
24
25func (h *handler) DownloadVideo(ctx context.Context, videoID string, resolution string) error {
26 exists, err := h.db.HasVideo(ctx, videoID)
27 if err != nil {
28 return fmt.Errorf("failed to check video existence: %w", err)
29 }
30 if !exists {
31 return fmt.Errorf("video not found")
32 }
33
34 if err := h.db.SetVideoDownloadStatus(ctx, videoID, "pending"); err != nil {
35 return fmt.Errorf("failed to set download status: %w", err)
36 }
37
38 go h.performDownload(videoID, resolution)
39
40 return nil
41}
42
43func (h *handler) performDownload(videoID string, resolution string) {
44 ctx := context.Background()
45
46 video, err := h.db.GetVideo(ctx, videoID)
47 if err != nil {
48 h.log.Error("Failed to get video for download", "video_id", videoID, "error", err)
49 if dbErr := h.db.SetVideoDownloadFailed(ctx, videoID, "Failed to get video info"); dbErr != nil {
50 h.log.Error("Failed to update download status to failed", "video_id", videoID, "error", dbErr)
51 }
52 return
53 }
54
55 if err := h.db.SetVideoDownloadStatus(ctx, videoID, "downloading"); err != nil {
56 h.log.Error("Failed to update download status to downloading", "video_id", videoID, "error", err)
57 return
58 }
59
60 h.log.Info("Starting video download", "video_id", videoID, "title", video.Title, "resolution", resolution)
61
62 filePath, err := h.downloader.Download(ctx, videoID, video.Title, h.config.DownloadsDir, resolution)
63 if err != nil {
64 h.log.Error("Video download failed", "video_id", videoID, "error", err)
65 if dbErr := h.db.SetVideoDownloadFailed(ctx, videoID, err.Error()); dbErr != nil {
66 h.log.Error("Failed to update download status to failed", "video_id", videoID, "error", dbErr)
67 }
68 return
69 }
70
71 if err := h.db.SetVideoDownloadCompleted(ctx, videoID, filePath); err != nil {
72 h.log.Error("Failed to mark video as downloaded", "video_id", videoID, "error", err)
73 os.Remove(filePath)
74 if dbErr := h.db.SetVideoDownloadFailed(ctx, videoID, "Failed to update database"); dbErr != nil {
75 h.log.Error("Failed to update download status to failed", "video_id", videoID, "error", dbErr)
76 }
77 return
78 }
79
80 h.log.Info("Video downloaded successfully", "video_id", videoID, "path", filePath)
81}
82
83func (h *handler) ServeVideoFile(ctx context.Context, videoID string) (filePath string, filename string, err error) {
84 video, err := h.db.GetVideo(ctx, videoID)
85 if err != nil {
86 return "", "", fmt.Errorf("video not found: %w", err)
87 }
88
89 if !video.IsDownloaded() || video.FilePath == nil {
90 return "", "", fmt.Errorf("video not downloaded")
91 }
92
93 if _, err := os.Stat(*video.FilePath); os.IsNotExist(err) {
94 if dbErr := h.db.DeleteVideoFile(ctx, videoID); dbErr != nil {
95 h.log.Error("Failed to clear missing file from database", "video_id", videoID, "error", dbErr)
96 }
97 return "", "", fmt.Errorf("file not found on disk")
98 }
99
100 sanitizedTitle := sanitizeFilename(video.Title)
101 if sanitizedTitle == "" {
102 sanitizedTitle = videoID
103 }
104 ext := filepath.Ext(*video.FilePath)
105 filename = sanitizedTitle + ext
106
107 return *video.FilePath, filename, nil
108}