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

View File

@ -347,23 +347,25 @@ def parse_chapters(name, value):
# Cookies from browser # Cookies from browser
if opts.cookiesfrombrowser: if opts.cookiesfrombrowser:
container = None 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: if mobj is None:
raise ValueError(f'invalid cookies from browser arguments: {opts.cookiesfrombrowser}') 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() browser_name = browser_name.lower()
if browser_name not in SUPPORTED_BROWSERS: if browser_name not in SUPPORTED_BROWSERS:
raise ValueError(f'unsupported browser specified for cookies: "{browser_name}". ' raise ValueError(f'unsupported browser specified for cookies: "{browser_name}". '
f'Supported browsers are: {", ".join(sorted(SUPPORTED_BROWSERS))}') 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: if keyring is not None:
keyring = keyring.upper() keyring = keyring.upper()
if keyring not in SUPPORTED_KEYRINGS: if keyring not in SUPPORTED_KEYRINGS:
raise ValueError(f'unsupported keyring specified for cookies: "{keyring}". ' raise ValueError(f'unsupported keyring specified for cookies: "{keyring}". '
f'Supported keyrings are: {", ".join(sorted(SUPPORTED_KEYRINGS))}') 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 # MetadataParser
def metadataparser_actions(f): def metadataparser_actions(f):

View File

@ -128,9 +128,14 @@ def _extract_firefox_cookies(profile, container, logger):
else: else:
search_root = os.path.join(_firefox_browser_dir(), profile) 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 container_id = None
if container is not None: if container not in (None, 'none'):
containers_path = os.path.join(search_root, 'containers.json') 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): 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}') raise FileNotFoundError(f'could not read containers.json in {search_root}')
with open(containers_path, 'r') as containers: with open(containers_path, 'r') as containers:
@ -142,26 +147,21 @@ def _extract_firefox_cookies(profile, container, logger):
if not isinstance(container_id, int): if not isinstance(container_id, int):
raise ValueError(f'could not find firefox container "{container}" in containers.json') 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: with tempfile.TemporaryDirectory(prefix='yt_dlp') as tmpdir:
cursor = None cursor = None
try: try:
cursor = _open_database_copy(cookie_database_path, tmpdir) cursor = _open_database_copy(cookie_database_path, tmpdir)
origin_attributes = ''
if isinstance(container_id, int): if isinstance(container_id, int):
origin_attributes = f'^userContextId={container_id}'
logger.debug( logger.debug(
f'Only loading cookies from firefox container "{container}", ID {container_id}') f'Only loading cookies from firefox container "{container}", ID {container_id}')
try:
cursor.execute( cursor.execute(
'SELECT host, name, value, path, expiry, isSecure FROM moz_cookies WHERE originAttributes=?', 'SELECT host, name, value, path, expiry, isSecure FROM moz_cookies WHERE originAttributes LIKE ? OR originAttributes LIKE ?',
(origin_attributes, )) (f'%userContextId={container_id}', f'%userContextId={container_id}&%'))
except sqlite3.OperationalError: elif container == 'none':
logger.debug('Database exception, loading all cookies') 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') cursor.execute('SELECT host, name, value, path, expiry, isSecure FROM moz_cookies')
jar = YoutubeDLCookieJar() jar = YoutubeDLCookieJar()
with _create_progress_bar(logger) as progress_bar: 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)') help='Do not read/dump cookies from/to file (default)')
filesystem.add_option( filesystem.add_option(
'--cookies-from-browser', '--cookies-from-browser',
dest='cookiesfrombrowser', metavar='BROWSER[+KEYRING][:PROFILE[:CONTAINER]]', dest='cookiesfrombrowser', metavar='BROWSER[+KEYRING][:PROFILE][::CONTAINER]',
help=( help=(
'The name of the browser and (optionally) the name/path of the profile to load cookies from ' 'The name of the browser to load cookies from. '
'(and container name if Firefox) separated by a ":". '
f'Currently supported browsers are: {", ".join(sorted(SUPPORTED_BROWSERS))}. ' f'Currently supported browsers are: {", ".join(sorted(SUPPORTED_BROWSERS))}. '
'By default, the default container of the most recently accessed profile is used. ' 'Optionally, the KEYRING used for decrypting Chromium cookies on Linux, '
'The keyring used for decrypting Chromium cookies on Linux can be ' 'the name/path of the PROFILE to load cookies from, '
'(optionally) specified after the browser name separated by a "+". ' '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)))}')) f'Currently supported keyrings are: {", ".join(map(str.lower, sorted(SUPPORTED_KEYRINGS)))}'))
filesystem.add_option( filesystem.add_option(
'--no-cookies-from-browser', '--no-cookies-from-browser',