Add option --netrc-cmd
(#6682)
Authored by: NDagestad, pukkandan Closes #1706
This commit is contained in:
parent
af7585c824
commit
db3ad8a676
6 changed files with 58 additions and 28 deletions
15
README.md
15
README.md
|
@ -49,7 +49,7 @@ yt-dlp is a [youtube-dl](https://github.com/ytdl-org/youtube-dl) fork based on t
|
||||||
* [Extractor Options](#extractor-options)
|
* [Extractor Options](#extractor-options)
|
||||||
* [CONFIGURATION](#configuration)
|
* [CONFIGURATION](#configuration)
|
||||||
* [Configuration file encoding](#configuration-file-encoding)
|
* [Configuration file encoding](#configuration-file-encoding)
|
||||||
* [Authentication with .netrc file](#authentication-with-netrc-file)
|
* [Authentication with netrc](#authentication-with-netrc)
|
||||||
* [Notes about environment variables](#notes-about-environment-variables)
|
* [Notes about environment variables](#notes-about-environment-variables)
|
||||||
* [OUTPUT TEMPLATE](#output-template)
|
* [OUTPUT TEMPLATE](#output-template)
|
||||||
* [Output template examples](#output-template-examples)
|
* [Output template examples](#output-template-examples)
|
||||||
|
@ -910,6 +910,8 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
|
||||||
--netrc-location PATH Location of .netrc authentication data;
|
--netrc-location PATH Location of .netrc authentication data;
|
||||||
either the path or its containing directory.
|
either the path or its containing directory.
|
||||||
Defaults to ~/.netrc
|
Defaults to ~/.netrc
|
||||||
|
--netrc-cmd NETRC_CMD Command to execute to get the credentials
|
||||||
|
credentials for an extractor.
|
||||||
--video-password PASSWORD Video password (vimeo, youku)
|
--video-password PASSWORD Video password (vimeo, youku)
|
||||||
--ap-mso MSO Adobe Pass multiple-system operator (TV
|
--ap-mso MSO Adobe Pass multiple-system operator (TV
|
||||||
provider) identifier, use --ap-list-mso for
|
provider) identifier, use --ap-list-mso for
|
||||||
|
@ -1203,7 +1205,7 @@ The configuration files are decoded according to the UTF BOM if present, and in
|
||||||
|
|
||||||
If you want your file to be decoded differently, add `# coding: ENCODING` to the beginning of the file (e.g. `# coding: shift-jis`). There must be no characters before that, even spaces or BOM.
|
If you want your file to be decoded differently, add `# coding: ENCODING` to the beginning of the file (e.g. `# coding: shift-jis`). There must be no characters before that, even spaces or BOM.
|
||||||
|
|
||||||
### Authentication with `.netrc` file
|
### Authentication with netrc
|
||||||
|
|
||||||
You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every yt-dlp execution and prevent tracking plain text passwords in the shell command history. You can achieve this using a [`.netrc` file](https://stackoverflow.com/tags/.netrc/info) on a per-extractor basis. For that you will need to create a `.netrc` file in `--netrc-location` and restrict permissions to read/write by only you:
|
You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every yt-dlp execution and prevent tracking plain text passwords in the shell command history. You can achieve this using a [`.netrc` file](https://stackoverflow.com/tags/.netrc/info) on a per-extractor basis. For that you will need to create a `.netrc` file in `--netrc-location` and restrict permissions to read/write by only you:
|
||||||
```
|
```
|
||||||
|
@ -1223,6 +1225,15 @@ To activate authentication with the `.netrc` file you should pass `--netrc` to y
|
||||||
|
|
||||||
The default location of the .netrc file is `~` (see below).
|
The default location of the .netrc file is `~` (see below).
|
||||||
|
|
||||||
|
As an alternative to using the `.netrc` file, which has the disadvantage of keeping your passwords in a plain text file, you can configure a custom shell command to provide the credentials for an extractor. This is done by providing the `--netrc-cmd` parameter, it shall output the credentials in the netrc format and return `0` on success, other values will be treated as an error. `{}` in the command will be replaced by the name of the extractor to make it possible to select the credentials for the right extractor.
|
||||||
|
To use braces in the command, they need to be escaped by doubling them. (see example bellow)
|
||||||
|
|
||||||
|
E.g. To use an encrypted `.netrc` file stored as `.authinfo.gpg`
|
||||||
|
```
|
||||||
|
yt-dlp --netrc-cmd 'gpg --decrypt ~/.authinfo.gpg' https://www.youtube.com/watch?v=BaW_jenozKc
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Notes about environment variables
|
### Notes about environment variables
|
||||||
* Environment variables are normally specified as `${VARIABLE}`/`$VARIABLE` on UNIX and `%VARIABLE%` on Windows; but is always shown as `${VARIABLE}` in this documentation
|
* Environment variables are normally specified as `${VARIABLE}`/`$VARIABLE` on UNIX and `%VARIABLE%` on Windows; but is always shown as `${VARIABLE}` in this documentation
|
||||||
* yt-dlp also allow using UNIX-style variables on Windows for path-like options; e.g. `--output`, `--config-location`
|
* yt-dlp also allow using UNIX-style variables on Windows for path-like options; e.g. `--output`, `--config-location`
|
||||||
|
|
|
@ -190,6 +190,7 @@ class YoutubeDL:
|
||||||
ap_password: Multiple-system operator account password.
|
ap_password: Multiple-system operator account password.
|
||||||
usenetrc: Use netrc for authentication instead.
|
usenetrc: Use netrc for authentication instead.
|
||||||
netrc_location: Location of the netrc file. Defaults to ~/.netrc.
|
netrc_location: Location of the netrc file. Defaults to ~/.netrc.
|
||||||
|
netrc_cmd: Use a shell command to get credentials
|
||||||
verbose: Print additional info to stdout.
|
verbose: Print additional info to stdout.
|
||||||
quiet: Do not print messages to stdout.
|
quiet: Do not print messages to stdout.
|
||||||
no_warnings: Do not print out anything for warnings.
|
no_warnings: Do not print out anything for warnings.
|
||||||
|
|
|
@ -188,8 +188,8 @@ def validate_options(opts):
|
||||||
raise ValueError(f'{max_name} "{max_val}" must be must be greater than or equal to {min_name} "{min_val}"')
|
raise ValueError(f'{max_name} "{max_val}" must be must be greater than or equal to {min_name} "{min_val}"')
|
||||||
|
|
||||||
# Usernames and passwords
|
# Usernames and passwords
|
||||||
validate(not opts.usenetrc or (opts.username is None and opts.password is None),
|
validate(sum(map(bool, (opts.usenetrc, opts.netrc_cmd, opts.username))) <= 1, '.netrc',
|
||||||
'.netrc', msg='using {name} conflicts with giving username/password')
|
msg='{name}, netrc command and username/password are mutually exclusive options')
|
||||||
validate(opts.password is None or opts.username is not None, 'account username', msg='{name} missing')
|
validate(opts.password is None or opts.username is not None, 'account username', msg='{name} missing')
|
||||||
validate(opts.ap_password is None or opts.ap_username is not None,
|
validate(opts.ap_password is None or opts.ap_username is not None,
|
||||||
'TV Provider account username', msg='{name} missing')
|
'TV Provider account username', msg='{name} missing')
|
||||||
|
@ -741,6 +741,7 @@ def parse_options(argv=None):
|
||||||
return ParsedOptions(parser, opts, urls, {
|
return ParsedOptions(parser, opts, urls, {
|
||||||
'usenetrc': opts.usenetrc,
|
'usenetrc': opts.usenetrc,
|
||||||
'netrc_location': opts.netrc_location,
|
'netrc_location': opts.netrc_location,
|
||||||
|
'netrc_cmd': opts.netrc_cmd,
|
||||||
'username': opts.username,
|
'username': opts.username,
|
||||||
'password': opts.password,
|
'password': opts.password,
|
||||||
'twofactor': opts.twofactor,
|
'twofactor': opts.twofactor,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import netrc
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import types
|
import types
|
||||||
|
@ -34,6 +35,7 @@ from ..utils import (
|
||||||
GeoUtils,
|
GeoUtils,
|
||||||
HEADRequest,
|
HEADRequest,
|
||||||
LenientJSONDecoder,
|
LenientJSONDecoder,
|
||||||
|
Popen,
|
||||||
RegexNotFoundError,
|
RegexNotFoundError,
|
||||||
RetryManager,
|
RetryManager,
|
||||||
UnsupportedError,
|
UnsupportedError,
|
||||||
|
@ -70,6 +72,7 @@ from ..utils import (
|
||||||
smuggle_url,
|
smuggle_url,
|
||||||
str_or_none,
|
str_or_none,
|
||||||
str_to_int,
|
str_to_int,
|
||||||
|
netrc_from_content,
|
||||||
strip_or_none,
|
strip_or_none,
|
||||||
traverse_obj,
|
traverse_obj,
|
||||||
truncate_string,
|
truncate_string,
|
||||||
|
@ -535,7 +538,7 @@ class InfoExtractor:
|
||||||
_EMBED_REGEX = []
|
_EMBED_REGEX = []
|
||||||
|
|
||||||
def _login_hint(self, method=NO_DEFAULT, netrc=None):
|
def _login_hint(self, method=NO_DEFAULT, netrc=None):
|
||||||
password_hint = f'--username and --password, or --netrc ({netrc or self._NETRC_MACHINE}) to provide account credentials'
|
password_hint = f'--username and --password, --netrc-cmd, or --netrc ({netrc or self._NETRC_MACHINE}) to provide account credentials'
|
||||||
return {
|
return {
|
||||||
None: '',
|
None: '',
|
||||||
'any': f'Use --cookies, --cookies-from-browser, {password_hint}',
|
'any': f'Use --cookies, --cookies-from-browser, {password_hint}',
|
||||||
|
@ -1291,45 +1294,47 @@ class InfoExtractor:
|
||||||
return clean_html(res)
|
return clean_html(res)
|
||||||
|
|
||||||
def _get_netrc_login_info(self, netrc_machine=None):
|
def _get_netrc_login_info(self, netrc_machine=None):
|
||||||
username = None
|
|
||||||
password = None
|
|
||||||
netrc_machine = netrc_machine or self._NETRC_MACHINE
|
netrc_machine = netrc_machine or self._NETRC_MACHINE
|
||||||
|
|
||||||
if self.get_param('usenetrc', False):
|
cmd = self.get_param('netrc_cmd', '').format(netrc_machine)
|
||||||
try:
|
if cmd:
|
||||||
|
self.to_screen(f'Executing command: {cmd}')
|
||||||
|
stdout, _, ret = Popen.run(cmd, text=True, shell=True, stdout=subprocess.PIPE)
|
||||||
|
if ret != 0:
|
||||||
|
raise OSError(f'Command returned error code {ret}')
|
||||||
|
info = netrc_from_content(stdout).authenticators(netrc_machine)
|
||||||
|
|
||||||
|
elif self.get_param('usenetrc', False):
|
||||||
netrc_file = compat_expanduser(self.get_param('netrc_location') or '~')
|
netrc_file = compat_expanduser(self.get_param('netrc_location') or '~')
|
||||||
if os.path.isdir(netrc_file):
|
if os.path.isdir(netrc_file):
|
||||||
netrc_file = os.path.join(netrc_file, '.netrc')
|
netrc_file = os.path.join(netrc_file, '.netrc')
|
||||||
info = netrc.netrc(file=netrc_file).authenticators(netrc_machine)
|
info = netrc.netrc(netrc_file).authenticators(netrc_machine)
|
||||||
if info is not None:
|
|
||||||
username = info[0]
|
|
||||||
password = info[2]
|
|
||||||
else:
|
|
||||||
raise netrc.NetrcParseError(
|
|
||||||
'No authenticators for %s' % netrc_machine)
|
|
||||||
except (OSError, netrc.NetrcParseError) as err:
|
|
||||||
self.report_warning(
|
|
||||||
'parsing .netrc: %s' % error_to_compat_str(err))
|
|
||||||
|
|
||||||
return username, password
|
else:
|
||||||
|
return None, None
|
||||||
|
if not info:
|
||||||
|
raise netrc.NetrcParseError(f'No authenticators for {netrc_machine}')
|
||||||
|
return info[0], info[2]
|
||||||
|
|
||||||
def _get_login_info(self, username_option='username', password_option='password', netrc_machine=None):
|
def _get_login_info(self, username_option='username', password_option='password', netrc_machine=None):
|
||||||
"""
|
"""
|
||||||
Get the login info as (username, password)
|
Get the login info as (username, password)
|
||||||
First look for the manually specified credentials using username_option
|
First look for the manually specified credentials using username_option
|
||||||
and password_option as keys in params dictionary. If no such credentials
|
and password_option as keys in params dictionary. If no such credentials
|
||||||
available look in the netrc file using the netrc_machine or _NETRC_MACHINE
|
are available try the netrc_cmd if it is defined or look in the
|
||||||
value.
|
netrc file using the netrc_machine or _NETRC_MACHINE value.
|
||||||
If there's no info available, return (None, None)
|
If there's no info available, return (None, None)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Attempt to use provided username and password or .netrc data
|
|
||||||
username = self.get_param(username_option)
|
username = self.get_param(username_option)
|
||||||
if username is not None:
|
if username is not None:
|
||||||
password = self.get_param(password_option)
|
password = self.get_param(password_option)
|
||||||
else:
|
else:
|
||||||
|
try:
|
||||||
username, password = self._get_netrc_login_info(netrc_machine)
|
username, password = self._get_netrc_login_info(netrc_machine)
|
||||||
|
except (OSError, netrc.NetrcParseError) as err:
|
||||||
|
self.report_warning(f'Failed to parse .netrc: {err}')
|
||||||
|
return None, None
|
||||||
return username, password
|
return username, password
|
||||||
|
|
||||||
def _get_tfa_info(self, note='two-factor verification code'):
|
def _get_tfa_info(self, note='two-factor verification code'):
|
||||||
|
|
|
@ -720,6 +720,10 @@ def create_parser():
|
||||||
'--netrc-location',
|
'--netrc-location',
|
||||||
dest='netrc_location', metavar='PATH',
|
dest='netrc_location', metavar='PATH',
|
||||||
help='Location of .netrc authentication data; either the path or its containing directory. Defaults to ~/.netrc')
|
help='Location of .netrc authentication data; either the path or its containing directory. Defaults to ~/.netrc')
|
||||||
|
authentication.add_option(
|
||||||
|
'--netrc-cmd',
|
||||||
|
dest='netrc_cmd', metavar='NETRC_CMD',
|
||||||
|
help='Command to execute to get the credentials for an extractor.')
|
||||||
authentication.add_option(
|
authentication.add_option(
|
||||||
'--video-password',
|
'--video-password',
|
||||||
dest='videopassword', metavar='PASSWORD',
|
dest='videopassword', metavar='PASSWORD',
|
||||||
|
|
|
@ -25,6 +25,7 @@ import json
|
||||||
import locale
|
import locale
|
||||||
import math
|
import math
|
||||||
import mimetypes
|
import mimetypes
|
||||||
|
import netrc
|
||||||
import operator
|
import operator
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
|
@ -864,6 +865,13 @@ def escapeHTML(text):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class netrc_from_content(netrc.netrc):
|
||||||
|
def __init__(self, content):
|
||||||
|
self.hosts, self.macros = {}, {}
|
||||||
|
with io.StringIO(content) as stream:
|
||||||
|
self._parse('-', stream, False)
|
||||||
|
|
||||||
|
|
||||||
def process_communicate_or_kill(p, *args, **kwargs):
|
def process_communicate_or_kill(p, *args, **kwargs):
|
||||||
deprecation_warning(f'"{__name__}.process_communicate_or_kill" is deprecated and may be removed '
|
deprecation_warning(f'"{__name__}.process_communicate_or_kill" is deprecated and may be removed '
|
||||||
f'in a future version. Use "{__name__}.Popen.communicate_or_kill" instead')
|
f'in a future version. Use "{__name__}.Popen.communicate_or_kill" instead')
|
||||||
|
|
Loading…
Reference in a new issue