Portable Configuration file (closes #19)

Inspired by https://github.com/ytdl-org/youtube-dl/pull/27592
This commit is contained in:
pukkandan 2021-01-16 23:51:00 +05:30
parent 30a074c2b6
commit e2e43aea21
3 changed files with 90 additions and 58 deletions

1
.gitignore vendored
View file

@ -46,6 +46,7 @@ updates_key.pem
*.swf *.swf
*.part *.part
*.ytdl *.ytdl
*.conf
*.swp *.swp
*.spec *.spec
test/local_parameters.json test/local_parameters.json

View file

@ -633,7 +633,20 @@ ## Extractor Options:
# CONFIGURATION # CONFIGURATION
You can configure youtube-dlc by placing any supported command line option to a configuration file. On Linux and macOS, the system wide configuration file is located at `/etc/youtube-dlc.conf` and the user wide configuration file at `~/.config/youtube-dlc/config`. On Windows, the user wide configuration file locations are `%APPDATA%\youtube-dlc\config.txt` or `C:\Users\<user name>\youtube-dlc.conf`. Note that by default configuration file may not exist so you may need to create it yourself. You can configure youtube-dlc by placing any supported command line option to a configuration file. The configuration is loaded from the following locations:
1. The file given by `--config-location`
1. **Portable Configuration**: `yt-dlp.conf` or `youtube-dlc.conf` in the same directory as the bundled binary. If you are running from source-code (`<root dir>/youtube_dlc/__main__.py`), the root directory is used instead.
1. **User Configuration**:
* `%XDG_CONFIG_HOME%/yt-dlp/config` (recommended on Linux/macOS)
* `%XDG_CONFIG_HOME%/yt-dlp.conf`
* `%APPDATA%/yt-dlp/config` (recommended on Windows)
* `%APPDATA%/yt-dlp/config.txt`
* `~/yt-dlp.conf`
* `~/yt-dlp.conf.txt`
If none of these files are found, the search is performed again by replacing `yt-dlp` with `youtube-dlc`. Note that `~` points to `C:\Users\<user name>` on windows. Also, `%XDG_CONFIG_HOME%` defaults to `~/.config` if undefined
1. **System Configuration**: `/etc/yt-dlp.conf` or `/etc/youtube-dlc.conf`
For example, with the following configuration file youtube-dlc will always extract the audio, not copy the mtime, use a proxy and save all videos under `Movies` directory in your home directory: For example, with the following configuration file youtube-dlc will always extract the audio, not copy the mtime, use a proxy and save all videos under `Movies` directory in your home directory:
``` ```
@ -652,11 +665,9 @@ # Save all videos under Movies directory in your home directory
-o ~/Movies/%(title)s.%(ext)s -o ~/Movies/%(title)s.%(ext)s
``` ```
Note that options in configuration file are just the same options aka switches used in regular command line calls thus there **must be no whitespace** after `-` or `--`, e.g. `-o` or `--proxy` but not `- o` or `-- proxy`. Note that options in configuration file are just the same options aka switches used in regular command line calls; thus there **must be no whitespace** after `-` or `--`, e.g. `-o` or `--proxy` but not `- o` or `-- proxy`.
You can use `--ignore-config` if you want to disable the configuration file for a particular youtube-dlc run. You can use `--ignore-config` if you want to disable all configuration files for a particular youtube-dlc run. If `--ignore-config` is found inside any configuration file, no further configuration will be loaded. For example, having the option in the portable configuration file prevents loading of user and system configurations. Additionally, (for backward compatibility) if `--ignore-config` is found inside the system configuration file, the user configuration is not loaded.
You can also use `--config-location` if you want to use custom configuration file for a particular youtube-dlc run.
### Authentication with `.netrc` file ### Authentication with `.netrc` file

View file

@ -54,43 +54,36 @@ def _readOptions(filename_bytes, default=[]):
optionf.close() optionf.close()
return res return res
def _readUserConf(): def _readUserConf(package_name, default=[]):
xdg_config_home = compat_getenv('XDG_CONFIG_HOME') # .config
if xdg_config_home: xdg_config_home = compat_getenv('XDG_CONFIG_HOME') or compat_expanduser('~/.config')
userConfFile = os.path.join(xdg_config_home, 'youtube-dlc', 'config') userConfFile = os.path.join(xdg_config_home, package_name, 'config')
if not os.path.isfile(userConfFile): if not os.path.isfile(userConfFile):
userConfFile = os.path.join(xdg_config_home, 'youtube-dlc.conf') userConfFile = os.path.join(xdg_config_home, '%s.conf' % package_name)
else: userConf = _readOptions(userConfFile, default=None)
userConfFile = os.path.join(compat_expanduser('~'), '.config', 'youtube-dlc', 'config') if userConf is not None:
if not os.path.isfile(userConfFile): return userConf
userConfFile = os.path.join(compat_expanduser('~'), '.config', 'youtube-dlc.conf')
userConf = _readOptions(userConfFile, None)
if userConf is None: # appdata
appdata_dir = compat_getenv('appdata') appdata_dir = compat_getenv('appdata')
if appdata_dir: if appdata_dir:
userConf = _readOptions( userConfFile = os.path.join(appdata_dir, package_name, 'config')
os.path.join(appdata_dir, 'youtube-dlc', 'config'), userConf = _readOptions(userConfFile, default=None)
default=None)
if userConf is None: if userConf is None:
userConf = _readOptions( userConf = _readOptions('%s.txt' % userConfFile, default=None)
os.path.join(appdata_dir, 'youtube-dlc', 'config.txt'), if userConf is not None:
default=None)
if userConf is None:
userConf = _readOptions(
os.path.join(compat_expanduser('~'), 'youtube-dlc.conf'),
default=None)
if userConf is None:
userConf = _readOptions(
os.path.join(compat_expanduser('~'), 'youtube-dlc.conf.txt'),
default=None)
if userConf is None:
userConf = []
return userConf return userConf
# home
userConfFile = os.path.join(compat_expanduser('~'), '%s.conf' % package_name)
userConf = _readOptions(userConfFile, default=None)
if userConf is None:
userConf = _readOptions('%s.txt' % userConfFile, default=None)
if userConf is not None:
return userConf
return default
def _format_option_string(option): def _format_option_string(option):
''' ('-o', '--option') -> -o, --format METAVAR''' ''' ('-o', '--option') -> -o, --format METAVAR'''
@ -1147,33 +1140,60 @@ def compat_conf(conf):
return [a.decode(preferredencoding(), 'replace') for a in conf] return [a.decode(preferredencoding(), 'replace') for a in conf]
return conf return conf
command_line_conf = compat_conf(sys.argv[1:]) configs = {
opts, args = parser.parse_args(command_line_conf) 'command_line': compat_conf(sys.argv[1:]),
'custom': [], 'portable': [], 'user': [], 'system': []}
opts, args = parser.parse_args(configs['command_line'])
system_conf = user_conf = custom_conf = [] def get_configs():
if '--config-location' in configs['command_line']:
if '--config-location' in command_line_conf:
location = compat_expanduser(opts.config_location) location = compat_expanduser(opts.config_location)
if os.path.isdir(location): if os.path.isdir(location):
location = os.path.join(location, 'youtube-dlc.conf') location = os.path.join(location, 'youtube-dlc.conf')
if not os.path.exists(location): if not os.path.exists(location):
parser.error('config-location %s does not exist.' % location) parser.error('config-location %s does not exist.' % location)
custom_conf = _readOptions(location) configs['custom'] = _readOptions(location)
elif '--ignore-config' in command_line_conf:
pass
else:
system_conf = _readOptions('/etc/youtube-dlc.conf')
if '--ignore-config' not in system_conf:
user_conf = _readUserConf()
argv = system_conf + user_conf + custom_conf + command_line_conf if '--ignore-config' in configs['command_line']:
return
if '--ignore-config' in configs['custom']:
return
def get_portable_path():
path = os.path.dirname(sys.argv[0])
if os.path.abspath(sys.argv[0]) != os.path.abspath(sys.executable): # Not packaged
path = os.path.join(path, '..')
return os.path.abspath(path)
run_path = get_portable_path()
configs['portable'] = _readOptions(os.path.join(run_path, 'yt-dlp.conf'), default=None)
if configs['portable'] is None:
configs['portable'] = _readOptions(os.path.join(run_path, 'youtube-dlc.conf'))
if '--ignore-config' in configs['portable']:
return
configs['system'] = _readOptions('/etc/yt-dlp.conf', default=None)
if configs['system'] is None:
configs['system'] = _readOptions('/etc/youtube-dlc.conf')
if '--ignore-config' in configs['system']:
return
configs['user'] = _readUserConf('yt-dlp', default=None)
if configs['user'] is None:
configs['user'] = _readUserConf('youtube-dlc')
if '--ignore-config' in configs['user']:
configs['system'] = []
get_configs()
argv = configs['system'] + configs['user'] + configs['portable'] + configs['custom'] + configs['command_line']
opts, args = parser.parse_args(argv) opts, args = parser.parse_args(argv)
if opts.verbose: if opts.verbose:
for conf_label, conf in ( for conf_label, conf in (
('System config', system_conf), ('System config', configs['system']),
('User config', user_conf), ('User config', configs['user']),
('Custom config', custom_conf), ('Portable config', configs['portable']),
('Command-line args', command_line_conf)): ('Custom config', configs['custom']),
('Command-line args', configs['command_line'])):
write_string('[debug] %s: %s\n' % (conf_label, repr(_hide_login_info(conf)))) write_string('[debug] %s: %s\n' % (conf_label, repr(_hide_login_info(conf))))
return parser, opts, args return parser, opts, args