@@ -29,8 +29,8 @@ var downloadExamples = `
2929Usage examples:
3030
3131photoprism dl --cookies cookies.txt \
32- --add- header 'Authorization: Bearer <token>' \
33- --dl- method file --file -remux auto -- \
32+ --header 'Authorization: Bearer <token>' \
33+ --method file --remux auto -- \
3434 https://example.com/a.mp4 https://example.com/b.jpg
3535
3636photoprism dl -a 'Authorization: Bearer <token>' \
@@ -50,31 +50,37 @@ var DownloadCommand = &cli.Command{
5050 Usage : "relative originals `PATH` in which new files should be imported" ,
5151 },
5252 & cli.StringFlag {
53- Name : "cookies" ,
54- Aliases : []string {"c" },
55- Usage : "use Netscape-format cookies.txt `FILE` for HTTP authentication" ,
56- },
57- & cli.StringSliceFlag {
58- Name : "add-header" ,
59- Aliases : []string {"a" },
60- Usage : "add HTTP request `HEADER` in the form 'Name: Value' (repeatable)" ,
53+ Name : "impersonate" ,
54+ Aliases : []string {"i" },
55+ Usage : "impersonate browser `IDENTITY` (e.g. chrome, edge or safari; 'none' to disable)" ,
56+ Value : "firefox" ,
6157 },
6258 & cli.StringFlag {
63- Name : "dl- method" ,
59+ Name : "method" ,
6460 Aliases : []string {"m" },
6561 Value : "pipe" ,
6662 Usage : "download `METHOD` when using external commands: pipe (stdio stream) or file (temporary files)" ,
6763 },
6864 & cli.StringFlag {
69- Name : "file- remux" ,
65+ Name : "remux" ,
7066 Aliases : []string {"r" },
7167 Value : "auto" ,
72- Usage : "remux `POLICY` for videos when using --dl- method file: auto (skip if MP4), always, or skip" ,
68+ Usage : "remux `POLICY` for videos when using --method file: auto (skip if MP4), always, or skip" ,
7369 },
7470 & cli.StringFlag {
75- Name : "format- sort" ,
71+ Name : "sort" ,
7672 Aliases : []string {"s" },
77- Usage : "custom FORMAT sort expression passed to yt-dlp" ,
73+ Usage : "custom `FORMAT` sort expression, e.g. 'quality,res,fps,codec:avc:m4a,size,br,asr,proto,ext,hasaud,source,id'" ,
74+ },
75+ & cli.StringFlag {
76+ Name : "cookies" ,
77+ Aliases : []string {"c" },
78+ Usage : "use Netscape-format cookies.txt `FILE` for HTTP authentication" ,
79+ },
80+ & cli.StringSliceFlag {
81+ Name : "header" ,
82+ Aliases : []string {"a" },
83+ Usage : "add HTTP request `HEADER` in the form 'Name: Value' (repeatable)" ,
7884 },
7985 },
8086 Action : downloadAction ,
@@ -148,29 +154,47 @@ func downloadAction(ctx *cli.Context) error {
148154
149155 // Flags for yt-dlp auth and headers
150156 cookies := strings .TrimSpace (ctx .String ("cookies" ))
157+
151158 // cookiesFromBrowser := strings.TrimSpace(ctx.String("cookies-from-browser"))
152- addHeaders := ctx .StringSlice ("add-header" )
159+ addHeaders := ctx .StringSlice ("header" )
160+
161+ impersonate := strings .ToLower (strings .TrimSpace (ctx .String ("impersonate" )))
162+
163+ if impersonate == "" {
164+ impersonate = "firefox"
165+ } else if impersonate == "none" {
166+ impersonate = ""
167+ }
168+
153169 flagMethod := ""
154- if ctx .IsSet ("dl-method" ) {
155- flagMethod = ctx .String ("dl-method" )
170+
171+ if ctx .IsSet ("method" ) {
172+ flagMethod = ctx .String ("method" )
156173 }
174+
157175 method , _ , err := resolveDownloadMethod (flagMethod )
176+
158177 if err != nil {
159178 return err
160179 }
161- formatSort := strings .TrimSpace (ctx .String ("format-sort" ))
180+
181+ formatSort := strings .TrimSpace (ctx .String ("sort" ))
162182 sortingFormat := formatSort
183+
163184 if sortingFormat == "" && method == "pipe" {
164185 sortingFormat = pipeSortingFormat
165186 }
166- fileRemux := strings .ToLower (strings .TrimSpace (ctx .String ("file-remux" )))
187+
188+ fileRemux := strings .ToLower (strings .TrimSpace (ctx .String ("remux" )))
189+
167190 if fileRemux == "" {
168191 fileRemux = "auto"
169192 }
193+
170194 switch fileRemux {
171195 case "always" , "auto" , "skip" :
172196 default :
173- return fmt .Errorf ("invalid --file- remux: %s (expected 'always', 'auto', or 'skip')" , fileRemux )
197+ return fmt .Errorf ("invalid --remux: %s (expected 'always', 'auto', or 'skip')" , fileRemux )
174198 }
175199
176200 // Process inputs sequentially (Phase 1)
@@ -210,27 +234,34 @@ func downloadAction(ctx *cli.Context) error {
210234 log .Infof ("downloading %s from %s" , mt , clean .Log (u .String ()))
211235
212236 opt := dl.Options {
213- MergeOutputFormat : fs .VideoMp4 .String (),
214- RemuxVideo : fs .VideoMp4 .String (),
215- SortingFormat : sortingFormat ,
216- Cookies : cookies ,
217- AddHeaders : addHeaders ,
237+ SortingFormat : sortingFormat ,
238+ Cookies : cookies ,
239+ AddHeaders : addHeaders ,
240+ Impersonate : impersonate ,
241+ }
242+ ytRemux := method != "pipe"
243+ if ytRemux {
244+ opt .MergeOutputFormat = fs .VideoMp4 .String ()
245+ opt .RemuxVideo = fs .VideoMp4 .String ()
218246 }
219247
220- result , err := dl .NewMetadata (context .Background (), u .String (), opt )
221- if err != nil {
222- log .Errorf ("metadata failed: %v" , err )
223- if hint , ok := missingFormatsHint (err ); ok {
248+ result , metaErr := dl .NewMetadata (context .Background (), u .String (), opt )
249+
250+ if metaErr != nil {
251+ log .Errorf ("metadata failed: %v" , metaErr )
252+ if hint , ok := missingFormatsHint (metaErr ); ok {
224253 log .Info (hint )
225254 }
226255 failures ++
227256 continue
228257 }
229258
230259 // Best-effort creation time for file method when not remuxing locally.
231- if created := dl .CreatedFromInfo (result .Info ); ! created .IsZero () {
232- // Apply via yt-dlp ffmpeg post-processor so creation_time exists even without our remux.
233- result .Options .FFmpegPostArgs = "-metadata creation_time=" + created .UTC ().Format (time .RFC3339 )
260+ if ytRemux {
261+ if created := dl .CreatedFromInfo (result .Info ); ! created .IsZero () {
262+ // Apply via yt-dlp ffmpeg post-processor so creation_time exists even without our remux.
263+ result .Options .FFmpegPostArgs = "-metadata creation_time=" + created .UTC ().Format (time .RFC3339 )
264+ }
234265 }
235266
236267 // Base filename for pipe method
@@ -243,17 +274,9 @@ func downloadAction(ctx *cli.Context) error {
243274
244275 if method == "pipe" {
245276 // Stream to stdout
246- downloadResult , err := result .DownloadWithOptions (context .Background (), dl.DownloadOptions {
247- Filter : "best" ,
248- DownloadAudioOnly : false ,
249- EmbedMetadata : true ,
250- EmbedSubs : false ,
251- ForceOverwrites : false ,
252- DisableCaching : false ,
253- PlaylistIndex : 1 ,
254- })
255- if err != nil {
256- log .Errorf ("download failed: %v" , err )
277+ downloadResult , dlErr := dl .Download (context .Background (), u .String (), opt , "best" )
278+ if dlErr != nil {
279+ log .Errorf ("download failed: %v" , dlErr )
257280 failures ++
258281 continue
259282 }
@@ -285,7 +308,7 @@ func downloadAction(ctx *cli.Context) error {
285308 // file method
286309 // Deterministic output template within the session temp dir
287310 outTpl := filepath .Join (downloadPath , "ppdl_%(id)s.%(ext)s" )
288- files , err := result .DownloadToFileWithOptions (context .Background (), dl.DownloadOptions {
311+ files , dlErr := result .DownloadToFileWithOptions (context .Background (), dl.DownloadOptions {
289312 Filter : "best" ,
290313 DownloadAudioOnly : false ,
291314 EmbedMetadata : true ,
@@ -295,10 +318,12 @@ func downloadAction(ctx *cli.Context) error {
295318 PlaylistIndex : 1 ,
296319 Output : outTpl ,
297320 })
298- if err != nil {
299- log .Errorf ("download failed: %v" , err )
321+
322+ if dlErr != nil {
323+ log .Errorf ("download failed: %v" , dlErr )
300324 // even on error, any completed files returned will be imported
301325 }
326+
302327 // Ensure container/metadata per remux policy for file method
303328 if fileRemux != "skip" {
304329 for _ , fp := range files {
@@ -325,6 +350,7 @@ func downloadAction(ctx *cli.Context) error {
325350 w .Start (opt )
326351
327352 elapsed := time .Since (start )
353+
328354 if failures > 0 {
329355 log .Warnf ("completed with %d error(s) in %s" , failures , elapsed )
330356 } else {
@@ -334,5 +360,6 @@ func downloadAction(ctx *cli.Context) error {
334360 if failures > 0 {
335361 return fmt .Errorf ("some downloads failed: %d" , failures )
336362 }
363+
337364 return nil
338365}
0 commit comments