preset.go
⎇
Raw
1package service
2
3import (
4 "fmt"
5 "strings"
6 "vidarchive/internal/models"
7 "vidarchive/internal/repository"
8)
9
10type PresetService struct {
11 repo *repository.PresetRepository
12}
13
14func NewPresetService(repo *repository.PresetRepository) *PresetService {
15 return &PresetService{repo: repo}
16}
17
18func (s *PresetService) Create(p *models.Preset) error {
19 return s.repo.Save(p)
20}
21
22func (s *PresetService) GetByID(id int64) (*models.Preset, error) {
23 return s.repo.GetByID(id)
24}
25
26func (s *PresetService) GetAll() ([]*models.Preset, error) {
27 return s.repo.GetAll()
28}
29
30func (s *PresetService) GetDefault() (*models.Preset, error) {
31 return s.repo.GetDefault()
32}
33
34func (s *PresetService) Update(p *models.Preset) error {
35 return s.repo.Save(p)
36}
37
38func (s *PresetService) Delete(id int64) error {
39 return s.repo.Delete(id)
40}
41
42func (s *PresetService) BuildArgs(p *models.Preset, formatOverride, customFlags string) []string {
43 var args []string
44
45 // Format selection
46 if formatOverride != "" {
47 args = append(args, "-f", formatOverride)
48 } else if p.FormatMode == "custom" && p.CustomFormat != "" {
49 args = append(args, "-f", p.CustomFormat)
50 } else if p.FormatMode == "preset" && p.Format != "" {
51 if p.Quality != "" {
52 args = append(args, "-f", applyHeightCap(p.Format, p.Quality))
53 } else {
54 args = append(args, "-f", p.Format)
55 }
56 }
57 // If FormatMode is "default" or empty, don't pass -f (let yt-dlp choose)
58
59 // Audio extraction
60 if p.ExtractAudio {
61 args = append(args, "--extract-audio")
62 if p.AudioFormat != "" {
63 args = append(args, "--audio-format", p.AudioFormat)
64 }
65 }
66
67 // Subtitles
68 if p.EmbedSubs {
69 args = append(args, "--embed-subs")
70 if p.SubLangs != "" {
71 args = append(args, "--sub-langs", p.SubLangs)
72 }
73 }
74
75 // Thumbnail
76 if p.EmbedThumbnail {
77 args = append(args, "--embed-thumbnail")
78 }
79
80 // Metadata
81 if p.EmbedMetadata {
82 args = append(args, "--embed-metadata")
83 }
84
85 // Info JSON
86 if p.WriteInfoJSON {
87 args = append(args, "--write-info-json")
88 }
89
90 // Custom flags
91 if p.CustomFlags != "" {
92 args = append(args, strings.Fields(p.CustomFlags)...)
93 }
94
95 if customFlags != "" {
96 args = append(args, strings.Fields(customFlags)...)
97 }
98
99 return args
100}
101
102// applyHeightCap adds a max-height filter to a format selector. For a combined
103// "video+audio" selector it binds the filter to the video stream only (e.g.
104// "bestvideo[height<=720]+bestaudio"), since applying it to the whole expression
105// would wrongly constrain the audio selector. It always appends an unfiltered
106// fallback so a missing capped variant still resolves.
107func applyHeightCap(format, maxHeight string) string {
108 parts := strings.Split(format, "+")
109 parts[0] = fmt.Sprintf("%s[height<=%s]", parts[0], maxHeight)
110 return strings.Join(parts, "+") + "/" + format
111}
112
113func (s *PresetService) EffectiveFlags(p *models.Preset, formatOverride, customFlags string) string {
114 args := s.BuildArgs(p, formatOverride, customFlags)
115 return strings.Join(args, " ")
116}
117