preset_test.go
⎇
Raw
1package service
2
3import (
4 "testing"
5
6 "vidarchive/internal/models"
7)
8
9func TestPresetServiceBuildArgs(t *testing.T) {
10 // Create a mock preset
11 preset := &models.Preset{
12 FormatMode: "preset",
13 Format: "best",
14 Quality: "1080",
15 ExtractAudio: true,
16 AudioFormat: "mp3",
17 EmbedSubs: true,
18 SubLangs: "en,de",
19 EmbedThumbnail: true,
20 EmbedMetadata: true,
21 WriteInfoJSON: true,
22 CustomFlags: "--no-playlist",
23 }
24
25 svc := &PresetService{}
26 args := svc.BuildArgs(preset, "", "")
27
28 // Check that expected args are present
29 hasFormat := false
30 hasAudio := false
31 hasSubs := false
32 hasOutput := false
33
34 for i := 0; i < len(args); i++ {
35 switch args[i] {
36 case "-f":
37 if i+1 < len(args) {
38 if args[i+1] == "best[height<=1080]/best" {
39 hasFormat = true
40 }
41 }
42 case "--extract-audio":
43 hasAudio = true
44 case "--embed-subs":
45 hasSubs = true
46 case "-o":
47 if i+1 < len(args) {
48 if args[i+1] == "item-%(autonumber)05d/%(title)s.%(ext)s" {
49 hasOutput = true
50 }
51 }
52 }
53 }
54
55 if !hasFormat {
56 t.Error("expected format arg with quality limit")
57 }
58 if !hasAudio {
59 t.Error("expected --extract-audio flag")
60 }
61 if !hasSubs {
62 t.Error("expected --embed-subs flag")
63 }
64 if hasOutput {
65 t.Error("output template should not be added by BuildArgs; it is enforced by the download service")
66 }
67}
68
69func TestPresetServiceBuildArgsWithOverride(t *testing.T) {
70 preset := &models.Preset{
71 Format: "best",
72 }
73
74 svc := &PresetService{}
75 args := svc.BuildArgs(preset, "22", "--verbose")
76
77 // Format override should take precedence
78 hasOverride := false
79 hasCustom := false
80
81 for i := 0; i < len(args); i++ {
82 if args[i] == "-f" && i+1 < len(args) && args[i+1] == "22" {
83 hasOverride = true
84 }
85 if args[i] == "--verbose" {
86 hasCustom = true
87 }
88 }
89
90 if !hasOverride {
91 t.Error("expected format override to be present")
92 }
93 if !hasCustom {
94 t.Error("expected custom flags to be present")
95 }
96}
97
98// yt-dlp separates extractor arguments with ";" — a "," separates the values of
99// one argument, so joining with it makes max_comments part of the sort value.
100func TestPresetServiceBuildArgsCommentExtractorArgs(t *testing.T) {
101 cases := []struct {
102 name string
103 preset *models.Preset
104 wantExtractor []string
105 }{
106 {
107 name: "comment args use a semicolon",
108 preset: &models.Preset{WriteComments: true, CommentSort: "top", MaxComments: 25},
109 wantExtractor: []string{"youtube:comment_sort=top;max_comments=25"},
110 },
111 {
112 // Two --extractor-args for the same key would make the second replace
113 // the first, so youtube arguments must be merged into one flag.
114 name: "youtube extra args are merged",
115 preset: &models.Preset{
116 WriteComments: true,
117 CommentSort: "top",
118 MaxComments: 25,
119 CommentExtractorArgs: "youtube:player_client=web",
120 },
121 wantExtractor: []string{"youtube:comment_sort=top;max_comments=25;player_client=web"},
122 },
123 {
124 name: "another extractor stays separate",
125 preset: &models.Preset{
126 WriteComments: true,
127 CommentSort: "top",
128 CommentExtractorArgs: "vimeo:foo=bar",
129 },
130 wantExtractor: []string{"youtube:comment_sort=top", "vimeo:foo=bar"},
131 },
132 }
133
134 for _, tc := range cases {
135 t.Run(tc.name, func(t *testing.T) {
136 args := (&PresetService{}).BuildArgs(tc.preset, "", "")
137 var got []string
138 for i, arg := range args {
139 if arg == "--extractor-args" && i+1 < len(args) {
140 got = append(got, args[i+1])
141 }
142 }
143 if len(got) != len(tc.wantExtractor) {
144 t.Fatalf("--extractor-args values = %v, want %v", got, tc.wantExtractor)
145 }
146 for i, want := range tc.wantExtractor {
147 if got[i] != want {
148 t.Errorf("--extractor-args[%d] = %q, want %q", i, got[i], want)
149 }
150 }
151 })
152 }
153}
154
155// A quoted value must survive as a single argument instead of being split on
156// spaces, which would hand yt-dlp three broken arguments.
157func TestPresetServiceBuildArgsKeepsQuotedCustomFlag(t *testing.T) {
158 args := (&PresetService{}).BuildArgs(&models.Preset{}, "", `--match-filter "duration > 60"`)
159 want := []string{"--match-filter", "duration > 60"}
160 if len(args) != len(want) {
161 t.Fatalf("args = %v, want %v", args, want)
162 }
163 for i := range want {
164 if args[i] != want[i] {
165 t.Errorf("args[%d] = %q, want %q", i, args[i], want[i])
166 }
167 }
168}
169
170func TestPresetServiceIgnoresWhitespaceCommentSort(t *testing.T) {
171 preset := &models.Preset{WriteComments: true, CommentSort: " "}
172 flags := (&PresetService{}).EffectiveFlags(preset, "", "")
173 if contains(flags, "comment_sort=") {
174 t.Fatalf("whitespace-only comment sort emitted an empty argument: %q", flags)
175 }
176}
177
178func TestPresetServiceEffectiveFlags(t *testing.T) {
179 preset := &models.Preset{
180 FormatMode: "preset",
181 Format: "best",
182 Quality: "720",
183 }
184
185 svc := &PresetService{}
186 flags := svc.EffectiveFlags(preset, "", "")
187
188 if flags == "" {
189 t.Error("expected non-empty flags string")
190 }
191 if flags != "-f best[height<=720]/best" {
192 t.Errorf("expected '-f best[height<=720]/best', got %q", flags)
193 }
194}
195
196func TestPresetServiceEffectiveFlagsWithCustom(t *testing.T) {
197 preset := &models.Preset{
198 FormatMode: "preset",
199 Format: "best",
200 CustomFlags: "--no-warnings",
201 }
202
203 svc := &PresetService{}
204 flags := svc.EffectiveFlags(preset, "", "--verbose")
205
206 if flags == "" {
207 t.Error("expected non-empty flags string")
208 }
209 // Should contain both preset custom flags and passed custom flags
210 if !contains(flags, "--no-warnings") {
211 t.Errorf("expected flags to contain '--no-warnings', got %q", flags)
212 }
213 if !contains(flags, "--verbose") {
214 t.Errorf("expected flags to contain '--verbose', got %q", flags)
215 }
216}
217
218func contains(s, substr string) bool {
219 return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsHelper(s, substr))
220}
221
222func containsHelper(s, substr string) bool {
223 for i := 0; i <= len(s)-len(substr); i++ {
224 if s[i:i+len(substr)] == substr {
225 return true
226 }
227 }
228 return false
229}
230