Add support for loading a config file with different parameters for each site

On Unix it look for ~/.config/youtube-dl/config.ini or ~/.config/youtube-dl.ini, Windows support is currently missing.
The new config file is in the INI format, the options are given in the long form without the leading dashes.
Parameters for an specific extractor are written in their own section.
When a video is contained in a playlist, it inherits the parameters from the playlist extractor.

Example:

    write-thumbnail=

    [youtube:playlist]
    output=%(playlist)s/%(id)s.%(ext)s

    [youtube]
    format=webm

All the videos use '--write-thumbnail', videos inside a YouTube playlist use the specified output template and YouTube videos are downloaded in the webm format.
This commit is contained in:
Jaime Marquínez Ferrándiz 2016-03-19 16:39:20 +01:00
parent fa5bda9053
commit e6f20cd142
3 changed files with 78 additions and 5 deletions

View File

@ -42,6 +42,7 @@ from .downloader import (
)
from .extractor import gen_extractors, list_extractors
from .YoutubeDL import YoutubeDL
from .params import Params, ParamsSection
def _build_ydl_opts(opts, parser):
@ -311,6 +312,30 @@ def _build_ydl_opts(opts, parser):
}
class CLIParams(Params):
def __init__(self, opts, build_section_args, parser):
super(CLIParams, self).__init__(_build_ydl_opts(opts, parser))
self.build_section_args = build_section_args
self.parser = parser
def section(self, section):
return CLIParamsSection([section], self.build_section_args, self.parser)
# TODO: investigate if it's worth to cache this
class CLIParamsSection(ParamsSection):
def __init__(self, sections, build_section_args, parser):
section_args = build_section_args(*sections)
section_opts = parser.parse_args(section_args)[0]
super(CLIParamsSection, self).__init__(_build_ydl_opts(section_opts, parser), {})
self.section_names = sections
self.build_section_args = build_section_args
self.parser = parser
def section(self, section):
return CLIParamsSection(self.section_names + [section], self.build_section_args, self.parser)
def _real_main(argv=None):
# Compatibility fixes for Windows
if sys.platform == 'win32':
@ -321,7 +346,7 @@ def _real_main(argv=None):
setproctitle('youtube-dl')
parser, opts, args = parseOpts(argv)
parser, opts, build_section_args, args = parseOpts(argv)
# Set user agent
if opts.user_agent is not None:
@ -385,7 +410,7 @@ def _real_main(argv=None):
compat_print(desc)
sys.exit(0)
ydl_opts = _build_ydl_opts(opts, parser)
ydl_opts = CLIParams(opts, build_section_args, parser)
with YoutubeDL(ydl_opts) as ydl:
# Update version

View File

@ -582,11 +582,17 @@ if sys.version_info >= (3, 0):
else:
from tokenize import generate_tokens as compat_tokenize_tokenize
try:
import configparser as compat_configparser
except ImportError:
import ConfigParser as compat_configparser
__all__ = [
'compat_HTMLParser',
'compat_HTTPError',
'compat_basestring',
'compat_chr',
'compat_configparser',
'compat_cookiejar',
'compat_cookies',
'compat_etree_fromstring',

View File

@ -1,11 +1,13 @@
from __future__ import unicode_literals
import io
import os.path
import optparse
import sys
from .downloader.external import list_external_downloaders
from .compat import (
compat_configparser,
compat_expanduser,
compat_get_terminal_size,
compat_getenv,
@ -743,17 +745,41 @@ def parseOpts(overrideArguments=None):
optionf.close()
return res
def _readIni(filename_bytes, default=([], {})):
try:
ini = open(filename_bytes)
except IOError:
return default # silently skip if file is not present
parser = compat_configparser.RawConfigParser()
ini = io.StringIO('[@GLOBAL@]\n' + ini.read())
parser.readfp(ini)
def convert_opts(opts):
return [
'--' + opt + ('' if not arg else ('=' + arg))
for opt, arg in opts]
global_opts = convert_opts(parser.items('@GLOBAL@'))
section_opts = dict((section, convert_opts(parser.items(section))) for section in parser.sections() if section != '@GLOBAL@')
return global_opts, section_opts
def _readUserConf():
xdg_config_home = compat_getenv('XDG_CONFIG_HOME')
if xdg_config_home:
userConfFile = os.path.join(xdg_config_home, 'youtube-dl', 'config')
userIniFile = os.path.join(xdg_config_home, 'youtube-dl', 'config.ini')
if not os.path.isfile(userConfFile):
userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
if not os.path.isfile(userIniFile):
userIniFile = os.path.join(xdg_config_home, 'youtube-dl.ini')
else:
userConfFile = os.path.join(compat_expanduser('~'), '.config', 'youtube-dl', 'config')
userIniFile = os.path.join(compat_expanduser('~'), '.config', 'youtube-dl', 'config.ini')
if not os.path.isfile(userConfFile):
userConfFile = os.path.join(compat_expanduser('~'), '.config', 'youtube-dl.conf')
if not os.path.isfile(userIniFile):
userIniFile = os.path.join(compat_expanduser('~'), '.config', 'youtube-dl.ini')
userConf = _readOptions(userConfFile, None)
userIni = _readIni(userIniFile, None)
if userConf is None:
appdata_dir = compat_getenv('appdata')
@ -778,7 +804,11 @@ def parseOpts(overrideArguments=None):
if userConf is None:
userConf = []
return userConf
if userIni is None:
userIni = [], {}
iniGlobal, iniSections = userIni
return userConf + iniGlobal, iniSections
def _hide_login_info(opts):
opts = list(opts)
@ -807,18 +837,30 @@ def parseOpts(overrideArguments=None):
if '--ignore-config' in command_line_conf:
system_conf = []
user_conf = []
user_sections = {}
else:
system_conf = compat_conf(_readOptions('/etc/youtube-dl.conf'))
if '--ignore-config' in system_conf:
user_conf = []
user_sections = {}
else:
user_conf = compat_conf(_readUserConf())
user_conf, user_sections = _readUserConf()
user_conf = compat_conf(user_conf)
argv = system_conf + user_conf + command_line_conf
opts, args = parser.parse_args(argv)
if opts.verbose:
write_string('[debug] System config: ' + repr(_hide_login_info(system_conf)) + '\n')
write_string('[debug] User config: ' + repr(_hide_login_info(user_conf)) + '\n')
for section, section_args in user_sections.items():
write_string('[debug] User config for "' + section + '": ' + repr(_hide_login_info(section_args)) + '\n')
write_string('[debug] Command-line args: ' + repr(_hide_login_info(command_line_conf)) + '\n')
return parser, opts, args
def build_section_args(*sections):
res = system_conf + user_conf
for section in sections:
res.extend(user_sections.get(section, []))
res.extend(command_line_conf)
return res
return parser, opts, build_section_args, args