Diaoul / subliminal

Subtitles, faster than your thoughts
http://subliminal.readthedocs.org
MIT License
2.42k stars 312 forks source link

Pass arguments to providers through the CLI #1179

Closed getzze closed 6 days ago

getzze commented 1 month ago

Right now we can pass arguments to the providers only through the config file. Because of the extension system, we cannot set the arguments of each and every providers as hard-coded options for the CLI. However, click allows to add dynamic options!

The scheme for the provider (and refiner) arguments I thought about is: --provider.opensubtitles.username=user

The variable names are _provider__opensubtitles__username, note the double underscores to be able to split the variables if an underscore is present in an argument name or provider name, for instance _provider__addic7ed__allow_searches.

We could also add support for passing options to the CLI with environment variables (easy to do with click). The scheme would be: SUBLIMINAL_PROVIDER_OPENSUBTITLES_USERNAME (with single underscores).

etherealite commented 1 month ago

How about allowing the provider and refiner classes to handle this?

from subliminal.providers import cli_option

class SomeProvider(Provider):
    @cli_option(
        nargs=2,
        metavar="USERNAME PASSWORD",
        help="OpenSubtitles configuration.",
    )
    def configure(toml_config: dict[str, Any], username: str, password: str):
        config = {**toml_config}

        if username:
            config["username"] = username
        return config

# back in the cli module
from subliminal.extensions import registered_options

def combine_provider_options(ctx, param, value):
    if not hasattr(ctx.obj, "provider_options"):
        ctx.obj.combined = {}
    ctx.obj.combined[param.name] = value
    return value

def provider_options(func):
    for entry_point, option in registered_options:
        option["callback"] = combine_provider_options
        # same as @providers_config.option('--opensubtitlles', type=click.STRING, nargs=2, etc.. )
        func = providers_config.option(f"--{entery_point}", **option)(func)

    return func

@provider_options
def subliminal(
    ctx: click.Context,
    cache_dir: str,
    debug: bool,
) -> None:
    provider_options = ctx.obj["provider_options"]
    provider_configs = ctx.obj["provider_configs"]
    for ep in extension_manager:
        provider_configs[ep.name] = ep.plugin.configure(
            provider_configs.get(ep.name, {}), *provider_options[ep.name]
        )
etherealite commented 1 month ago

Oh wait I didn't see you already merged a PR for this.