[cookies] Improve container support (#4806)

Closes #4800
Authored by: bashonly, pukkandan, coletdjnz
This commit is contained in:
bashonly 2022-09-01 09:52:59 +00:00 committed by GitHub
parent 92aa6d6883
commit 825d3ce386
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 39 deletions

View file

@ -706,19 +706,20 @@ ## Filesystem Options:
and dump cookie jar in
--no-cookies Do not read/dump cookies from/to file
(default)
--cookies-from-browser BROWSER[+KEYRING][:PROFILE[:CONTAINER]]
The name of the browser and (optionally) the
name/path of the profile to load cookies
from (and container name if Firefox)
separated by a ":". Currently supported
browsers are: brave, chrome, chromium, edge,
firefox, opera, safari, vivaldi. By default,
the default container of the most recently
accessed profile is used. The keyring used
for decrypting Chromium cookies on Linux can
be (optionally) specified after the browser
name separated by a "+". Currently supported
keyrings are: basictext, gnomekeyring, kwallet
--cookies-from-browser BROWSER[+KEYRING][:PROFILE][::CONTAINER]
The name of the browser to load cookies
from. Currently supported browsers are:
brave, chrome, chromium, edge, firefox,
opera, safari, vivaldi. Optionally, the
KEYRING used for decrypting Chromium cookies
on Linux, the name/path of the PROFILE to
load cookies from, and the CONTAINER name
(if Firefox) ("none" for no container) can
be given with their respective seperators.
By default, all containers of the most
recently accessed profile are used.
Currently supported keyrings are: basictext,
gnomekeyring, kwallet
--no-cookies-from-browser Do not load cookies from browser (default)
--cache-dir DIR Location in the filesystem where youtube-dl
can store some downloaded information (such

View file

@ -347,23 +347,25 @@ def parse_chapters(name, value):
# Cookies from browser
if opts.cookiesfrombrowser:
container = None
mobj = re.match(r'(?P<name>[^+:]+)(\s*\+\s*(?P<keyring>[^:]+))?(\s*:(?P<profile>.+))?', opts.cookiesfrombrowser)
mobj = re.fullmatch(r'''(?x)
(?P<name>[^+:]+)
(?:\s*\+\s*(?P<keyring>[^:]+))?
(?:\s*:\s*(?P<profile>.+?))?
(?:\s*::\s*(?P<container>.+))?
''', opts.cookiesfrombrowser)
if mobj is None:
raise ValueError(f'invalid cookies from browser arguments: {opts.cookiesfrombrowser}')
browser_name, keyring, profile = mobj.group('name', 'keyring', 'profile')
browser_name, keyring, profile, container = mobj.group('name', 'keyring', 'profile', 'container')
browser_name = browser_name.lower()
if browser_name not in SUPPORTED_BROWSERS:
raise ValueError(f'unsupported browser specified for cookies: "{browser_name}". '
f'Supported browsers are: {", ".join(sorted(SUPPORTED_BROWSERS))}')
elif profile and browser_name == 'firefox':
if ':' in profile and not os.path.exists(profile):
profile, container = profile.split(':', 1)
if keyring is not None:
keyring = keyring.upper()
if keyring not in SUPPORTED_KEYRINGS:
raise ValueError(f'unsupported keyring specified for cookies: "{keyring}". '
f'Supported keyrings are: {", ".join(sorted(SUPPORTED_KEYRINGS))}')
opts.cookiesfrombrowser = (browser_name, profile, keyring, container)
opts.cookiesfrombrowser = (browser_name, profile or None, keyring, container or None)
# MetadataParser
def metadataparser_actions(f):

View file

@ -128,9 +128,14 @@ def _extract_firefox_cookies(profile, container, logger):
else:
search_root = os.path.join(_firefox_browser_dir(), profile)
cookie_database_path = _find_most_recently_used_file(search_root, 'cookies.sqlite', logger)
if cookie_database_path is None:
raise FileNotFoundError(f'could not find firefox cookies database in {search_root}')
logger.debug(f'Extracting cookies from: "{cookie_database_path}"')
container_id = None
if container is not None:
containers_path = os.path.join(search_root, 'containers.json')
if container not in (None, 'none'):
containers_path = os.path.join(os.path.dirname(cookie_database_path), 'containers.json')
if not os.path.isfile(containers_path) or not os.access(containers_path, os.R_OK):
raise FileNotFoundError(f'could not read containers.json in {search_root}')
with open(containers_path, 'r') as containers:
@ -142,26 +147,21 @@ def _extract_firefox_cookies(profile, container, logger):
if not isinstance(container_id, int):
raise ValueError(f'could not find firefox container "{container}" in containers.json')
cookie_database_path = _find_most_recently_used_file(search_root, 'cookies.sqlite', logger)
if cookie_database_path is None:
raise FileNotFoundError(f'could not find firefox cookies database in {search_root}')
logger.debug(f'Extracting cookies from: "{cookie_database_path}"')
with tempfile.TemporaryDirectory(prefix='yt_dlp') as tmpdir:
cursor = None
try:
cursor = _open_database_copy(cookie_database_path, tmpdir)
origin_attributes = ''
if isinstance(container_id, int):
origin_attributes = f'^userContextId={container_id}'
logger.debug(
f'Only loading cookies from firefox container "{container}", ID {container_id}')
try:
cursor.execute(
'SELECT host, name, value, path, expiry, isSecure FROM moz_cookies WHERE originAttributes=?',
(origin_attributes, ))
except sqlite3.OperationalError:
logger.debug('Database exception, loading all cookies')
'SELECT host, name, value, path, expiry, isSecure FROM moz_cookies WHERE originAttributes LIKE ? OR originAttributes LIKE ?',
(f'%userContextId={container_id}', f'%userContextId={container_id}&%'))
elif container == 'none':
logger.debug('Only loading cookies not belonging to any container')
cursor.execute(
'SELECT host, name, value, path, expiry, isSecure FROM moz_cookies WHERE NOT INSTR(originAttributes,"userContextId=")')
else:
cursor.execute('SELECT host, name, value, path, expiry, isSecure FROM moz_cookies')
jar = YoutubeDLCookieJar()
with _create_progress_bar(logger) as progress_bar:

View file

@ -1400,14 +1400,15 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs):
help='Do not read/dump cookies from/to file (default)')
filesystem.add_option(
'--cookies-from-browser',
dest='cookiesfrombrowser', metavar='BROWSER[+KEYRING][:PROFILE[:CONTAINER]]',
dest='cookiesfrombrowser', metavar='BROWSER[+KEYRING][:PROFILE][::CONTAINER]',
help=(
'The name of the browser and (optionally) the name/path of the profile to load cookies from '
'(and container name if Firefox) separated by a ":". '
'The name of the browser to load cookies from. '
f'Currently supported browsers are: {", ".join(sorted(SUPPORTED_BROWSERS))}. '
'By default, the default container of the most recently accessed profile is used. '
'The keyring used for decrypting Chromium cookies on Linux can be '
'(optionally) specified after the browser name separated by a "+". '
'Optionally, the KEYRING used for decrypting Chromium cookies on Linux, '
'the name/path of the PROFILE to load cookies from, '
'and the CONTAINER name (if Firefox) ("none" for no container) '
'can be given with their respective seperators. '
'By default, all containers of the most recently accessed profile are used. '
f'Currently supported keyrings are: {", ".join(map(str.lower, sorted(SUPPORTED_KEYRINGS)))}'))
filesystem.add_option(
'--no-cookies-from-browser',