mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-10-31 06:35:12 +00:00 
			
		
		
		
	Add option --netrc-cmd (#6682)
				
					
				
			Authored by: NDagestad, pukkandan Closes #1706
This commit is contained in:
		
							
								
								
									
										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) | ||||
| * [CONFIGURATION](#configuration) | ||||
|     * [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) | ||||
| * [OUTPUT TEMPLATE](#output-template) | ||||
|     * [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; | ||||
|                                     either the path or its containing directory. | ||||
|                                     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) | ||||
|     --ap-mso MSO                    Adobe Pass multiple-system operator (TV | ||||
|                                     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. | ||||
| 
 | ||||
| ### 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: | ||||
| ``` | ||||
| @@ -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). | ||||
| 
 | ||||
| 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 | ||||
| * 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` | ||||
|   | ||||
| @@ -190,6 +190,7 @@ class YoutubeDL: | ||||
|     ap_password:       Multiple-system operator account password. | ||||
|     usenetrc:          Use netrc for authentication instead. | ||||
|     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. | ||||
|     quiet:             Do not print messages to stdout. | ||||
|     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}"') | ||||
| 
 | ||||
|     # Usernames and passwords | ||||
|     validate(not opts.usenetrc or (opts.username is None and opts.password is None), | ||||
|              '.netrc', msg='using {name} conflicts with giving username/password') | ||||
|     validate(sum(map(bool, (opts.usenetrc, opts.netrc_cmd, opts.username))) <= 1, '.netrc', | ||||
|              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.ap_password is None or opts.ap_username is not None, | ||||
|              'TV Provider account username', msg='{name} missing') | ||||
| @@ -741,6 +741,7 @@ def parse_options(argv=None): | ||||
|     return ParsedOptions(parser, opts, urls, { | ||||
|         'usenetrc': opts.usenetrc, | ||||
|         'netrc_location': opts.netrc_location, | ||||
|         'netrc_cmd': opts.netrc_cmd, | ||||
|         'username': opts.username, | ||||
|         'password': opts.password, | ||||
|         'twofactor': opts.twofactor, | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import netrc | ||||
| import os | ||||
| import random | ||||
| import re | ||||
| import subprocess | ||||
| import sys | ||||
| import time | ||||
| import types | ||||
| @@ -34,6 +35,7 @@ from ..utils import ( | ||||
|     GeoUtils, | ||||
|     HEADRequest, | ||||
|     LenientJSONDecoder, | ||||
|     Popen, | ||||
|     RegexNotFoundError, | ||||
|     RetryManager, | ||||
|     UnsupportedError, | ||||
| @@ -70,6 +72,7 @@ from ..utils import ( | ||||
|     smuggle_url, | ||||
|     str_or_none, | ||||
|     str_to_int, | ||||
|     netrc_from_content, | ||||
|     strip_or_none, | ||||
|     traverse_obj, | ||||
|     truncate_string, | ||||
| @@ -535,7 +538,7 @@ class InfoExtractor: | ||||
|     _EMBED_REGEX = [] | ||||
| 
 | ||||
|     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 { | ||||
|             None: '', | ||||
|             'any': f'Use --cookies, --cookies-from-browser, {password_hint}', | ||||
| @@ -1291,45 +1294,47 @@ class InfoExtractor: | ||||
|         return clean_html(res) | ||||
| 
 | ||||
|     def _get_netrc_login_info(self, netrc_machine=None): | ||||
|         username = None | ||||
|         password = None | ||||
|         netrc_machine = netrc_machine or self._NETRC_MACHINE | ||||
| 
 | ||||
|         if self.get_param('usenetrc', False): | ||||
|             try: | ||||
|         cmd = self.get_param('netrc_cmd', '').format(netrc_machine) | ||||
|         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 '~') | ||||
|             if os.path.isdir(netrc_file): | ||||
|                 netrc_file = os.path.join(netrc_file, '.netrc') | ||||
|                 info = netrc.netrc(file=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)) | ||||
|             info = netrc.netrc(netrc_file).authenticators(netrc_machine) | ||||
| 
 | ||||
|         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): | ||||
|         """ | ||||
|         Get the login info as (username, password) | ||||
|         First look for the manually specified credentials using username_option | ||||
|         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 | ||||
|         value. | ||||
|         are available try the netrc_cmd if it is defined or look in the | ||||
|         netrc file using the netrc_machine or _NETRC_MACHINE value. | ||||
|         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) | ||||
|         if username is not None: | ||||
|             password = self.get_param(password_option) | ||||
|         else: | ||||
|             try: | ||||
|                 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 | ||||
| 
 | ||||
|     def _get_tfa_info(self, note='two-factor verification code'): | ||||
|   | ||||
| @@ -720,6 +720,10 @@ def create_parser(): | ||||
|         '--netrc-location', | ||||
|         dest='netrc_location', metavar='PATH', | ||||
|         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( | ||||
|         '--video-password', | ||||
|         dest='videopassword', metavar='PASSWORD', | ||||
|   | ||||
| @@ -25,6 +25,7 @@ import json | ||||
| import locale | ||||
| import math | ||||
| import mimetypes | ||||
| import netrc | ||||
| import operator | ||||
| import os | ||||
| 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): | ||||
|     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') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Nicolai Dagestad
					Nicolai Dagestad