11import enum
22import json
3- import os . path
3+ import os
44import re
55import subprocess
66import sys
7+ import tempfile
78import time
89import uuid
910
@@ -42,6 +43,7 @@ class ExternalFD(FragmentFD):
4243 def real_download (self , filename , info_dict ):
4344 self .report_destination (filename )
4445 tmpfilename = self .temp_name (filename )
46+ self ._cookies_tempfile = None
4547
4648 try :
4749 started = time .time ()
@@ -54,6 +56,9 @@ def real_download(self, filename, info_dict):
5456 # should take place
5557 retval = 0
5658 self .to_screen ('[%s] Interrupted by user' % self .get_basename ())
59+ finally :
60+ if self ._cookies_tempfile :
61+ self .try_remove (self ._cookies_tempfile )
5762
5863 if retval == 0 :
5964 status = {
@@ -125,6 +130,16 @@ def _configuration_args(self, keys=None, *args, **kwargs):
125130 self .get_basename (), self .params .get ('external_downloader_args' ), self .EXE_NAME ,
126131 keys , * args , ** kwargs )
127132
133+ def _write_cookies (self ):
134+ if not self .ydl .cookiejar .filename :
135+ tmp_cookies = tempfile .NamedTemporaryFile (suffix = '.cookies' , delete = False )
136+ tmp_cookies .close ()
137+ self ._cookies_tempfile = tmp_cookies .name
138+ self .to_screen (f'[download] Writing temporary cookies file to "{ self ._cookies_tempfile } "' )
139+ # real_download resets _cookies_tempfile; if it's None then save() will write to cookiejar.filename
140+ self .ydl .cookiejar .save (self ._cookies_tempfile )
141+ return self .ydl .cookiejar .filename or self ._cookies_tempfile
142+
128143 def _call_downloader (self , tmpfilename , info_dict ):
129144 """ Either overwrite this or implement _make_cmd """
130145 cmd = [encodeArgument (a ) for a in self ._make_cmd (tmpfilename , info_dict )]
@@ -184,6 +199,8 @@ class CurlFD(ExternalFD):
184199
185200 def _make_cmd (self , tmpfilename , info_dict ):
186201 cmd = [self .exe , '--location' , '-o' , tmpfilename , '--compressed' ]
202+ if self .ydl .cookiejar .get_cookie_header (info_dict ['url' ]):
203+ cmd += ['--cookie-jar' , self ._write_cookies ()]
187204 if info_dict .get ('http_headers' ) is not None :
188205 for key , val in info_dict ['http_headers' ].items ():
189206 cmd += ['--header' , f'{ key } : { val } ' ]
@@ -214,6 +231,9 @@ def _make_cmd(self, tmpfilename, info_dict):
214231 if info_dict .get ('http_headers' ) is not None :
215232 for key , val in info_dict ['http_headers' ].items ():
216233 cmd += ['-H' , f'{ key } : { val } ' ]
234+ cookie_header = self .ydl .cookiejar .get_cookie_header (info_dict ['url' ])
235+ if cookie_header :
236+ cmd += [f'Cookie: { cookie_header } ' , '--max-redirect=0' ]
217237 cmd += self ._configuration_args ()
218238 cmd += ['--' , info_dict ['url' ]]
219239 return cmd
@@ -223,7 +243,9 @@ class WgetFD(ExternalFD):
223243 AVAILABLE_OPT = '--version'
224244
225245 def _make_cmd (self , tmpfilename , info_dict ):
226- cmd = [self .exe , '-O' , tmpfilename , '-nv' , '--no-cookies' , '--compression=auto' ]
246+ cmd = [self .exe , '-O' , tmpfilename , '-nv' , '--compression=auto' ]
247+ if self .ydl .cookiejar .get_cookie_header (info_dict ['url' ]):
248+ cmd += ['--load-cookies' , self ._write_cookies ()]
227249 if info_dict .get ('http_headers' ) is not None :
228250 for key , val in info_dict ['http_headers' ].items ():
229251 cmd += ['--header' , f'{ key } : { val } ' ]
@@ -279,6 +301,8 @@ def _make_cmd(self, tmpfilename, info_dict):
279301 else :
280302 cmd += ['--min-split-size' , '1M' ]
281303
304+ if self .ydl .cookiejar .get_cookie_header (info_dict ['url' ]):
305+ cmd += [f'--load-cookies={ self ._write_cookies ()} ' ]
282306 if info_dict .get ('http_headers' ) is not None :
283307 for key , val in info_dict ['http_headers' ].items ():
284308 cmd += ['--header' , f'{ key } : { val } ' ]
@@ -417,6 +441,14 @@ def _make_cmd(self, tmpfilename, info_dict):
417441 if info_dict .get ('http_headers' ) is not None :
418442 for key , val in info_dict ['http_headers' ].items ():
419443 cmd += [f'{ key } :{ val } ' ]
444+
445+ # httpie 3.1.0+ removes the Cookie header on redirect, so this should be safe for now. [1]
446+ # If we ever need cookie handling for redirects, we can export the cookiejar into a session. [2]
447+ # 1: https://github.com/httpie/httpie/security/advisories/GHSA-9w4w-cpc8-h2fq
448+ # 2: https://httpie.io/docs/cli/sessions
449+ cookie_header = self .ydl .cookiejar .get_cookie_header (info_dict ['url' ])
450+ if cookie_header :
451+ cmd += [f'Cookie:{ cookie_header } ' ]
420452 return cmd
421453
422454
@@ -527,6 +559,11 @@ def _call_downloader(self, tmpfilename, info_dict):
527559
528560 selected_formats = info_dict .get ('requested_formats' ) or [info_dict ]
529561 for i , fmt in enumerate (selected_formats ):
562+ cookies = self .ydl .cookiejar .get_cookies_for_url (fmt ['url' ])
563+ if cookies :
564+ args .extend (['-cookies' , '' .join (
565+ f'{ cookie .name } ={ cookie .value } ; path={ cookie .path } ; domain={ cookie .domain } ;\r \n '
566+ for cookie in cookies )])
530567 if fmt .get ('http_headers' ) and re .match (r'^https?://' , fmt ['url' ]):
531568 # Trailing \r\n after each HTTP header is important to prevent warning from ffmpeg/avconv:
532569 # [http @ 00000000003d2fa0] No trailing CRLF found in HTTP header.
0 commit comments