subliminal 2.4.0__tar.gz → 2.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {subliminal-2.4.0 → subliminal-2.5.0}/.pre-commit-config.yaml +4 -4
- {subliminal-2.4.0 → subliminal-2.5.0}/HISTORY.rst +27 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/PKG-INFO +3 -2
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/user/configuration_file.rst +4 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/hatch.toml +1 -1
- {subliminal-2.4.0 → subliminal-2.5.0}/pyproject.toml +2 -1
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/cli/generate_config.py +59 -28
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/core.py +2 -2
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/extensions.py +35 -11
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/opensubtitles.py +5 -3
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/opensubtitlescom.py +21 -6
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/score.py +1 -1
- subliminal-2.5.0/tests/cassettes/opensubtitlescom/test_query_max_result_pages.yaml +10748 -0
- subliminal-2.5.0/tests/cassettes/tmdb/test_search_movie_page.yaml +313 -0
- subliminal-2.5.0/tests/cli/test_cli.py +34 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cli/test_download.py +102 -110
- subliminal-2.5.0/tests/cli/test_generate_config.py +57 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/conftest.py +223 -4
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/providers/test_opensubtitlescom.py +15 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/providers/test_providers.py +23 -19
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/refiners/test_tmdb.py +8 -4
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_archives.py +4 -3
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_core.py +14 -3
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_provider_pool.py +19 -17
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_subtitle.py +1 -1
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_video.py +2 -1
- subliminal-2.4.0/tests/cassettes/tmdb/test_search_movie_page.yaml +0 -146
- subliminal-2.4.0/tests/cli/test_cli.py +0 -32
- subliminal-2.4.0/tests/cli/test_generate_config.py +0 -33
- {subliminal-2.4.0 → subliminal-2.5.0}/.dockerignore +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.github/actions/prepare-tests/action.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.github/workflows/CI.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.github/workflows/coverage.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.github/workflows/docker-build.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.github/workflows/prepare-release-pr.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.github/workflows/publish-release.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.github/workflows/publish.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.github/workflows/scheduled-tests.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.github/workflows/tag-release.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.github/zizmor.yml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.gitignore +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.python-version-default +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/.readthedocs.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/CONTRIBUTING.md +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/Dockerfile +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/LICENSE +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/MANIFEST.in +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/README.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/RELEASING.md +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/changelog.d/.gitignore +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/Makefile +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/_intersphinx/README.md +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/_intersphinx/importlib_metadata_objects.inv +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/_intersphinx/importlib_metadata_objects.txt +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/_static/.gitignore +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/cache.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/cli.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/core.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/exceptions.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/extensions.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/providers.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/refiners.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/score.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/subtitle.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/utils.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/api/video.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/cassettes/test_usage.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/conf.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/config.toml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/conftest.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/index.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/make.bat +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/user/changelog.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/user/cli.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/user/how_it_works.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/user/installation.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/user/provider_guide.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/docs/user/usage.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/scripts/.gitignore +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/scripts/prepare-release-pr.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/scripts/prepare_tests.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/scripts/release.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/__main__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/archives.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/cache.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/cli/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/cli/cli.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/cli/commands/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/cli/commands/_format.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/cli/commands/download_best.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/cli/helpers.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/converters/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/converters/addic7ed.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/converters/opensubtitles.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/converters/opensubtitlescom.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/converters/subtitulamos.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/converters/tvsubtitles.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/exceptions.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/matches.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/addic7ed.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/bsplayer.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/gestdown.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/mock.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/napiprojekt.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/podnapisi.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/subtitulamos.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/providers/tvsubtitles.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/py.typed +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/refiners/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/refiners/hash.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/refiners/metadata.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/refiners/omdb.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/refiners/tmdb.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/refiners/tvdb.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/subtitle.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/utils.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/src/subliminal/video.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_id_country.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_id_quote_dots_mixed_case.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_id_with_comma.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_id_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_ids.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_list_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_list_subtitles_show_with_asterisk.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_parsing.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_parsing_colon.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_parsing_dash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_parsing_quote_dots_mixed_case.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_wrong_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/bsplayer/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/bsplayer/test_list_subtitles_hash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/bsplayer/test_login.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/bsplayer/test_logout.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/bsplayer/test_query_hash_size.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/cli/test_cli_download.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_download_with_bom.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_get_title_and_show_id_alternative_name.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_get_title_and_show_id_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_get_title_and_show_id_only_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_get_title_and_show_id_with_tvdb_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_list_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_all_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_no_language.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_parsing.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_parsing_colon.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_parsing_dash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_parsing_quote_dots_mixed_case.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_wrong_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_id_error.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_id_incomplete.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_id_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_id_quote.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_series_tvdb_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/gestdown/test_show_with_asterisk.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_download_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_list_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_query_microdvd.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_query_srt.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_query_srt_reencode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_query_wrong_hash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_get_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_get_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_get_wrong_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_get_wrong_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_episode_original_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_episode_with_country.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_episode_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_movie_guess_alternative_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_search.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_search_page.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_search_type.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_search_wrong_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/omdb/test_search_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_list_subtitles_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_list_subtitles_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_list_subtitles_movie_no_hash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_login_bad_password.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_login_vip_bad_password.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_login_vip_login.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_no_operation.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_hash_size.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_imdb_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_not_enough_information.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_query_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_query_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_query_season_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_wrong_hash_wrong_size.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_movie_no_hash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_login.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_login_bad_password.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_logout.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_hash_size.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_imdb_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_query_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_query_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_query_season_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_tag_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_wrong_hash_wrong_size.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_tag_match.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_user_infos.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_download_subtitles_with_title_unicode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_list_subtitles_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_list_subtitles_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_query_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_query_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/providers/test_download_best_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/providers/test_download_best_subtitles_min_score.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/providers/test_download_best_subtitles_only_one.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/providers/test_list_subtitles_providers_download.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_download_subtitle_first_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_download_subtitle_foo.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_download_subtitle_last_season.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_download_subtitle_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_language.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_season.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_list_subtitles_with_spanish_non_mx_search.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_get_movie_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_get_movie_id_failed.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_get_series_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_get_series_wrong_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_episode_original_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_episode_with_country.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_episode_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_movie_guess_alternative_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_search_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_search_movie_wrong_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_search_movie_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_search_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tmdb/test_search_series_wrong_name.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_get_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_get_series_actors.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_get_series_actors_wrong_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_get_series_wrong_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_login.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_login_error.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_query_series_episodes.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_query_series_episodes_wrong_season.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_ambiguous.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_ambiguous_2.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_alternative_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_partial.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_with_comma.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_with_country.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refresh_token.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_search_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_search_series_wrong_name.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvdb/test_token_needs_refresh.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_download_subtitle_with_quote.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_get_episode_ids.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_get_episode_ids_wrong_season.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_list_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_query.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_query_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_query_with_quote.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_query_wrong_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_query_wrong_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_ambiguous.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_error.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_incomplete.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_uk.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_us.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_year_in_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/cli/test_helpers.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/providers/test_addic7ed.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/providers/test_bsplayer.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/providers/test_gestdown.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/providers/test_napiprojekt.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/providers/test_opensubtitles.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/providers/test_podnapisi.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/providers/test_subtitulamos.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/providers/test_tvsubtitles.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/refiners/test_hash.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/refiners/test_metadata.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/refiners/test_omdb.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/refiners/test_tvdb.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_cache.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_extensions.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_matches.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_provider.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_score.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tests/test_utils.py +0 -0
- {subliminal-2.4.0 → subliminal-2.5.0}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
repos:
|
|
2
2
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
-
rev:
|
|
3
|
+
rev: v6.0.0
|
|
4
4
|
hooks:
|
|
5
5
|
- id: check-toml
|
|
6
6
|
- id: check-yaml
|
|
@@ -8,12 +8,12 @@ repos:
|
|
|
8
8
|
- id: end-of-file-fixer
|
|
9
9
|
|
|
10
10
|
- repo: https://github.com/crate-ci/typos
|
|
11
|
-
rev: v1.
|
|
11
|
+
rev: v1.40.0
|
|
12
12
|
hooks:
|
|
13
13
|
- id: typos
|
|
14
14
|
|
|
15
15
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
16
|
-
rev: v0.
|
|
16
|
+
rev: v0.14.9
|
|
17
17
|
hooks:
|
|
18
18
|
- id: ruff
|
|
19
19
|
args: [--fix, --unsafe-fixes]
|
|
@@ -33,7 +33,7 @@ repos:
|
|
|
33
33
|
require_serial: true
|
|
34
34
|
|
|
35
35
|
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
|
36
|
-
rev: v1.
|
|
36
|
+
rev: v1.18.0
|
|
37
37
|
hooks:
|
|
38
38
|
- id: zizmor
|
|
39
39
|
|
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
.. towncrier release notes start
|
|
4
4
|
|
|
5
|
+
2.5.0 (2025-12-14)
|
|
6
|
+
^^^^^^^^^^^^^^^^^^
|
|
7
|
+
|
|
8
|
+
Provider Changes
|
|
9
|
+
----------------
|
|
10
|
+
|
|
11
|
+
- OpenSubtitlesCom: add a max_result_pages parameter to limit the number of result pages to speed up the search (`#1321 <https://github.com/Diaoul/subliminal/issues/1321>`__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Documentation
|
|
15
|
+
-------------
|
|
16
|
+
|
|
17
|
+
- fix ``generate_default_config`` for ``click==8.3`` and pin to ``click<8.3`` until another regression is fixed. (`#1318 <https://github.com/Diaoul/subliminal/issues/1318>`__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Misc
|
|
21
|
+
----
|
|
22
|
+
|
|
23
|
+
- add support for python 3.14 (`#1317 <https://github.com/Diaoul/subliminal/issues/1317>`__)
|
|
24
|
+
- Fix test with click 8.3.0 running in parallel (`#1320 <https://github.com/Diaoul/subliminal/issues/1320>`__)
|
|
25
|
+
- fix test on big-endian system (`#1323 <https://github.com/Diaoul/subliminal/issues/1323>`__)
|
|
26
|
+
- compatibility with stevedore 5.6.0 (`#1325 <https://github.com/Diaoul/subliminal/issues/1325>`__)
|
|
27
|
+
- fix some tests to pass in a dirty environment (`#1326 <https://github.com/Diaoul/subliminal/issues/1326>`__)
|
|
28
|
+
- update pre-commit hook versions (`#1329 <https://github.com/Diaoul/subliminal/issues/1329>`__)
|
|
29
|
+
- Fix some test so they are more reliable and stable (`#1331 <https://github.com/Diaoul/subliminal/issues/1331>`__)
|
|
30
|
+
|
|
31
|
+
|
|
5
32
|
2.4.0 (2025-09-03)
|
|
6
33
|
^^^^^^^^^^^^^^^^^^
|
|
7
34
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: subliminal
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: Subtitles, faster than your thoughts
|
|
5
5
|
Project-URL: homepage, https://github.com/Diaoul/subliminal
|
|
6
6
|
Project-URL: repository, https://github.com/Diaoul/subliminal
|
|
@@ -21,14 +21,15 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
25
|
Classifier: Topic :: Multimedia :: Video
|
|
25
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
27
|
Requires-Python: >=3.9
|
|
27
28
|
Requires-Dist: babelfish>=0.6.1
|
|
28
29
|
Requires-Dist: beautifulsoup4>=4.4.0
|
|
29
30
|
Requires-Dist: chardet>=5.0
|
|
31
|
+
Requires-Dist: click
|
|
30
32
|
Requires-Dist: click-option-group>=0.5.6
|
|
31
|
-
Requires-Dist: click>=8.0
|
|
32
33
|
Requires-Dist: defusedxml>=0.7.1
|
|
33
34
|
Requires-Dist: dogpile-cache>=1.0
|
|
34
35
|
Requires-Dist: guessit>=3.0.0
|
|
@@ -24,6 +24,10 @@ CLI arguments (like ``path``) cannot be specified in the configuration file.
|
|
|
24
24
|
|
|
25
25
|
Options that can be used multiple times (like ``--language``) need to be defined as arrays.
|
|
26
26
|
|
|
27
|
+
``hearing_impaired`` and ``foreign_only`` can take three values: "true", "false" or "[]".
|
|
28
|
+
They mean that hearing impaired (or foreign-only) subtitles will be, respectively,
|
|
29
|
+
ranked first, ranked last or not given any particular rank, for subtitles with identical scores.
|
|
30
|
+
|
|
27
31
|
|
|
28
32
|
Example
|
|
29
33
|
-------
|
|
@@ -80,7 +80,7 @@ description = "Run the tests suite"
|
|
|
80
80
|
features = ["tests"]
|
|
81
81
|
|
|
82
82
|
[[envs.tests.matrix]]
|
|
83
|
-
python = ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
83
|
+
python = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
84
84
|
|
|
85
85
|
[envs.tests.env-vars]
|
|
86
86
|
COVERAGE_PROCESS_START = "pyproject.toml"
|
|
@@ -29,6 +29,7 @@ classifiers = [
|
|
|
29
29
|
"Programming Language :: Python :: 3.11",
|
|
30
30
|
"Programming Language :: Python :: 3.12",
|
|
31
31
|
"Programming Language :: Python :: 3.13",
|
|
32
|
+
"Programming Language :: Python :: 3.14",
|
|
32
33
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
33
34
|
"Topic :: Multimedia :: Video",
|
|
34
35
|
]
|
|
@@ -38,7 +39,7 @@ dependencies = [
|
|
|
38
39
|
"beautifulsoup4>=4.4.0",
|
|
39
40
|
"defusedxml>=0.7.1",
|
|
40
41
|
"chardet>=5.0",
|
|
41
|
-
"click
|
|
42
|
+
"click",
|
|
42
43
|
"click-option-group>=0.5.6",
|
|
43
44
|
"dogpile.cache>=1.0",
|
|
44
45
|
"guessit>=3.0.0",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
+
from typing import Any
|
|
6
7
|
|
|
7
8
|
import click
|
|
8
9
|
import tomlkit
|
|
@@ -13,6 +14,61 @@ from .cli import subliminal
|
|
|
13
14
|
logger = logging.getLogger(__name__)
|
|
14
15
|
|
|
15
16
|
|
|
17
|
+
def _validate(name: str, value: Any) -> str | None:
|
|
18
|
+
"""Validate the value to be sure it is a valid TOML value.
|
|
19
|
+
|
|
20
|
+
E.g., Sentinel.UNSET was introduced in click 8.3 to be used as default value
|
|
21
|
+
and it is not convertible to valid TOML.
|
|
22
|
+
"""
|
|
23
|
+
# Create a stub table to try importing the key and value
|
|
24
|
+
stub = tomlkit.table()
|
|
25
|
+
try:
|
|
26
|
+
stub.add(name, value)
|
|
27
|
+
except (ValueError, tomlkit.exceptions.ConvertError):
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
return stub.as_string().strip('\n')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _add_value_to_table(
|
|
34
|
+
opt: click.Option,
|
|
35
|
+
table: tomlkit.items.Table,
|
|
36
|
+
*,
|
|
37
|
+
name: str | None = None,
|
|
38
|
+
commented: bool = True,
|
|
39
|
+
) -> str | None:
|
|
40
|
+
"""Add a value to a TOML table."""
|
|
41
|
+
if opt.name is None: # pragma: no cover
|
|
42
|
+
return None
|
|
43
|
+
# Override option name
|
|
44
|
+
opt_name = name if name is not None else opt.name
|
|
45
|
+
|
|
46
|
+
# Add the description of the option as a comment above
|
|
47
|
+
if opt.help:
|
|
48
|
+
table.add(tomlkit.comment(opt.help.capitalize()))
|
|
49
|
+
|
|
50
|
+
# Add the option name with default value
|
|
51
|
+
if opt.default is None:
|
|
52
|
+
# None value is not defined in TOML, it needs to be a comment
|
|
53
|
+
table.add(tomlkit.comment(f'{opt_name} = '))
|
|
54
|
+
|
|
55
|
+
elif (validated := _validate(opt_name, opt.default)) is not None:
|
|
56
|
+
# The default value is valid TOML
|
|
57
|
+
if commented:
|
|
58
|
+
# Add the string to the final table as a comment
|
|
59
|
+
table.add(tomlkit.comment(validated))
|
|
60
|
+
else:
|
|
61
|
+
# Add the key and valid value to the table
|
|
62
|
+
table.add(opt_name, opt.default)
|
|
63
|
+
|
|
64
|
+
else:
|
|
65
|
+
# The default value is not valid TOML, add a comment
|
|
66
|
+
table.add(tomlkit.comment(f'{opt_name} = '))
|
|
67
|
+
|
|
68
|
+
# Return the key to keep track of duplicates
|
|
69
|
+
return opt_name
|
|
70
|
+
|
|
71
|
+
|
|
16
72
|
def generate_default_config(*, compact: bool = True, commented: bool = True) -> str:
|
|
17
73
|
"""Generate a default configuration file.
|
|
18
74
|
|
|
@@ -20,31 +76,6 @@ def generate_default_config(*, compact: bool = True, commented: bool = True) ->
|
|
|
20
76
|
:param commented: if True, all the options are commented out.
|
|
21
77
|
:return: the default configuration as a string.
|
|
22
78
|
"""
|
|
23
|
-
|
|
24
|
-
def add_value_to_table(opt: click.Option, table: tomlkit.items.Table, *, name: str | None = None) -> str | None:
|
|
25
|
-
"""Add a value to a TOML table."""
|
|
26
|
-
if opt.name is None: # pragma: no cover
|
|
27
|
-
return None
|
|
28
|
-
# Override option name
|
|
29
|
-
opt_name = name if name is not None else opt.name
|
|
30
|
-
|
|
31
|
-
table.add(tomlkit.comment((opt.help or opt_name).capitalize()))
|
|
32
|
-
# table.add(tomlkit.comment(f'{opt_name} = {opt.default}'))
|
|
33
|
-
if opt.default is not None:
|
|
34
|
-
if not commented:
|
|
35
|
-
table.add(opt_name, opt.default)
|
|
36
|
-
else:
|
|
37
|
-
# Generate the entry in a dumb table
|
|
38
|
-
dumb = tomlkit.table()
|
|
39
|
-
dumb.add(opt_name, opt.default)
|
|
40
|
-
# Add the string to the final table as a comment
|
|
41
|
-
table.add(tomlkit.comment(dumb.as_string().strip('\n')))
|
|
42
|
-
else:
|
|
43
|
-
table.add(tomlkit.comment(f'{opt_name} = '))
|
|
44
|
-
|
|
45
|
-
# Return the key to keep track of duplicates
|
|
46
|
-
return opt_name
|
|
47
|
-
|
|
48
79
|
# Create TOML document
|
|
49
80
|
doc = tomlkit.document()
|
|
50
81
|
doc.add(tomlkit.comment('Subliminal default configuration file'))
|
|
@@ -62,7 +93,7 @@ def generate_default_config(*, compact: bool = True, commented: bool = True) ->
|
|
|
62
93
|
if opt.name in ['version', 'config']:
|
|
63
94
|
continue
|
|
64
95
|
# Add key=value to table
|
|
65
|
-
|
|
96
|
+
_add_value_to_table(opt, default, commented=commented)
|
|
66
97
|
if not compact: # pragma: no cover
|
|
67
98
|
default.add(tomlkit.nl())
|
|
68
99
|
# Adding the table to the document
|
|
@@ -85,7 +116,7 @@ def generate_default_config(*, compact: bool = True, commented: bool = True) ->
|
|
|
85
116
|
# Duplicated option
|
|
86
117
|
continue
|
|
87
118
|
# Add key=value to table
|
|
88
|
-
opt_name =
|
|
119
|
+
opt_name = _add_value_to_table(opt, com_table, commented=commented)
|
|
89
120
|
if opt_name is not None:
|
|
90
121
|
existing_options.add(opt_name)
|
|
91
122
|
if not compact: # pragma: no cover
|
|
@@ -114,7 +145,7 @@ def generate_default_config(*, compact: bool = True, commented: bool = True) ->
|
|
|
114
145
|
continue
|
|
115
146
|
|
|
116
147
|
# Add key=value to table
|
|
117
|
-
|
|
148
|
+
_add_value_to_table(opt, provider_table, name=opt_name, commented=commented)
|
|
118
149
|
if not compact: # pragma: no cover
|
|
119
150
|
provider_table.add(tomlkit.nl())
|
|
120
151
|
|
|
@@ -131,12 +131,12 @@ class ProviderPool:
|
|
|
131
131
|
|
|
132
132
|
"""
|
|
133
133
|
# check video validity
|
|
134
|
-
if not provider_manager[provider].plugin.check(video):
|
|
134
|
+
if not provider_manager[provider].plugin.check(video): # type: ignore[attr-defined]
|
|
135
135
|
logger.info('Skipping provider %r: not a valid video', provider)
|
|
136
136
|
return []
|
|
137
137
|
|
|
138
138
|
# check supported languages
|
|
139
|
-
provider_languages = provider_manager[provider].plugin.check_languages(languages)
|
|
139
|
+
provider_languages = provider_manager[provider].plugin.check_languages(languages) # type: ignore[attr-defined]
|
|
140
140
|
if not provider_languages:
|
|
141
141
|
logger.info('Skipping provider %r: no language to search for', provider)
|
|
142
142
|
return []
|
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import inspect
|
|
5
6
|
import re
|
|
6
7
|
from importlib.metadata import EntryPoint
|
|
7
8
|
from typing import TYPE_CHECKING, Any
|
|
8
9
|
|
|
9
|
-
from stevedore import ExtensionManager
|
|
10
|
+
from stevedore import ExtensionManager
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
12
13
|
from collections.abc import Sequence
|
|
13
14
|
|
|
15
|
+
from stevedore.extension import Extension
|
|
16
|
+
|
|
14
17
|
|
|
15
18
|
class RegistrableExtensionManager(ExtensionManager):
|
|
16
19
|
""":class:`stevedore.extension.ExtensionManager` with support for registration.
|
|
@@ -29,16 +32,21 @@ class RegistrableExtensionManager(ExtensionManager):
|
|
|
29
32
|
|
|
30
33
|
"""
|
|
31
34
|
|
|
35
|
+
#: Registered extensions with entry point syntax
|
|
32
36
|
registered_extensions: list[str]
|
|
37
|
+
|
|
38
|
+
#: Internal extensions with entry point syntax
|
|
33
39
|
internal_extensions: list[str]
|
|
34
40
|
|
|
41
|
+
_old_api: bool
|
|
42
|
+
|
|
35
43
|
def __init__(self, namespace: str, internal_extensions: Sequence[str], **kwargs: Any) -> None:
|
|
36
|
-
#: Registered extensions with entry point syntax
|
|
37
44
|
self.registered_extensions = []
|
|
38
|
-
|
|
39
|
-
#: Internal extensions with entry point syntax
|
|
40
45
|
self.internal_extensions = list(internal_extensions)
|
|
41
46
|
|
|
47
|
+
# 'verify_requirements' argument was remove in stevedore version 5.6.0
|
|
48
|
+
self._old_api = 'verify_requirements' in inspect.signature(self._load_one_plugin).parameters
|
|
49
|
+
|
|
42
50
|
super().__init__(namespace, **kwargs)
|
|
43
51
|
|
|
44
52
|
def list_entry_points(self) -> list[EntryPoint]:
|
|
@@ -60,6 +68,25 @@ class RegistrableExtensionManager(ExtensionManager):
|
|
|
60
68
|
|
|
61
69
|
return eps
|
|
62
70
|
|
|
71
|
+
def __compat_load_one_plugin(self, ep: EntryPoint) -> Extension | None: # pragma: no cover
|
|
72
|
+
if self._old_api:
|
|
73
|
+
# The 'verify_requirements' argument is needed
|
|
74
|
+
return self._load_one_plugin( # type: ignore[call-arg]
|
|
75
|
+
ep,
|
|
76
|
+
invoke_on_load=False,
|
|
77
|
+
invoke_args=(), # type: ignore[arg-type]
|
|
78
|
+
invoke_kwds={},
|
|
79
|
+
verify_requirements=False,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# The 'verify_requirements' argument is forbidden
|
|
83
|
+
return self._load_one_plugin( # type: ignore[call-arg]
|
|
84
|
+
ep,
|
|
85
|
+
invoke_on_load=False,
|
|
86
|
+
invoke_args=(), # type: ignore[arg-type]
|
|
87
|
+
invoke_kwds={},
|
|
88
|
+
)
|
|
89
|
+
|
|
63
90
|
def register(self, entry_point: str) -> None:
|
|
64
91
|
"""Register an extension.
|
|
65
92
|
|
|
@@ -76,13 +103,10 @@ class RegistrableExtensionManager(ExtensionManager):
|
|
|
76
103
|
msg = 'An extension with the same name already exist'
|
|
77
104
|
raise ValueError(msg)
|
|
78
105
|
|
|
79
|
-
ext = self.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
invoke_kwds={},
|
|
84
|
-
verify_requirements=False,
|
|
85
|
-
)
|
|
106
|
+
ext = self.__compat_load_one_plugin(ep)
|
|
107
|
+
if ext is None: # pragma: no cover
|
|
108
|
+
return
|
|
109
|
+
|
|
86
110
|
self.extensions.append(ext)
|
|
87
111
|
if self._extensions_by_name is not None: # pragma: no branch
|
|
88
112
|
self._extensions_by_name[ext.name] = ext
|
|
@@ -171,8 +171,9 @@ class OpenSubtitlesSubtitle(Subtitle):
|
|
|
171
171
|
class OpenSubtitlesProvider(Provider):
|
|
172
172
|
"""OpenSubtitles Provider.
|
|
173
173
|
|
|
174
|
-
:param str username: username
|
|
175
|
-
:param str password: password
|
|
174
|
+
:param str username: username
|
|
175
|
+
:param str password: password
|
|
176
|
+
:param int timeout: timeout in seconds. Default to 10.
|
|
176
177
|
|
|
177
178
|
"""
|
|
178
179
|
|
|
@@ -195,6 +196,7 @@ class OpenSubtitlesProvider(Provider):
|
|
|
195
196
|
username: str | None = None,
|
|
196
197
|
password: str | None = None,
|
|
197
198
|
*,
|
|
199
|
+
max_result_pages: int = 0,
|
|
198
200
|
timeout: int = 10,
|
|
199
201
|
) -> None:
|
|
200
202
|
transport = TimeoutSafeTransport(timeout=timeout, user_agent='VLSub')
|
|
@@ -279,7 +281,7 @@ class OpenSubtitlesProvider(Provider):
|
|
|
279
281
|
language = Language.fromopensubtitles(subtitle_item['SubLanguageID'])
|
|
280
282
|
hearing_impaired = bool(int(subtitle_item['SubHearingImpaired']))
|
|
281
283
|
page_link = subtitle_item['SubtitlesLink']
|
|
282
|
-
subtitle_id = int(subtitle_item['IDSubtitleFile'])
|
|
284
|
+
subtitle_id = str(int(subtitle_item['IDSubtitleFile']))
|
|
283
285
|
matched_by = subtitle_item['MatchedBy']
|
|
284
286
|
movie_kind = subtitle_item['MovieKind']
|
|
285
287
|
moviehash = subtitle_item['MovieHash']
|
|
@@ -370,8 +370,13 @@ def requires_auth(func: C) -> C:
|
|
|
370
370
|
class OpenSubtitlesComProvider(Provider):
|
|
371
371
|
"""OpenSubtitles.com Provider.
|
|
372
372
|
|
|
373
|
-
:param str username: username
|
|
374
|
-
:param str password: password
|
|
373
|
+
:param str username: username
|
|
374
|
+
:param str password: password
|
|
375
|
+
:param str apikey: private API key
|
|
376
|
+
:param int max_result_pages: maximum number of result pages to process,
|
|
377
|
+
set to non-zero to search faster, although the correct match can be
|
|
378
|
+
missing. Default to 0 (unlimited).
|
|
379
|
+
:param int timeout: timeout in seconds. Default to 20.
|
|
375
380
|
|
|
376
381
|
"""
|
|
377
382
|
|
|
@@ -385,6 +390,7 @@ class OpenSubtitlesComProvider(Provider):
|
|
|
385
390
|
username: str | None
|
|
386
391
|
password: str | None
|
|
387
392
|
apikey: str
|
|
393
|
+
max_result_pages: int
|
|
388
394
|
timeout: int
|
|
389
395
|
token_expires_at: datetime | None
|
|
390
396
|
session: Session | None
|
|
@@ -395,6 +401,7 @@ class OpenSubtitlesComProvider(Provider):
|
|
|
395
401
|
password: str | None = None,
|
|
396
402
|
*,
|
|
397
403
|
apikey: str | None = None,
|
|
404
|
+
max_result_pages: int = 0,
|
|
398
405
|
timeout: int = 20,
|
|
399
406
|
) -> None:
|
|
400
407
|
if any((username, password)) and not all((username, password)):
|
|
@@ -404,6 +411,7 @@ class OpenSubtitlesComProvider(Provider):
|
|
|
404
411
|
self.username = username
|
|
405
412
|
self.password = password
|
|
406
413
|
self.apikey = apikey or OPENSUBTITLESCOM_API_KEY
|
|
414
|
+
self.max_result_pages = max_result_pages
|
|
407
415
|
self.timeout = timeout
|
|
408
416
|
self.token_expires_at = None
|
|
409
417
|
self.session = None
|
|
@@ -588,19 +596,25 @@ class OpenSubtitlesComProvider(Provider):
|
|
|
588
596
|
return r.json() # type: ignore[no-any-return]
|
|
589
597
|
|
|
590
598
|
def _search(self, *, page: int = 1, **params: Any) -> list[dict[str, Any]]:
|
|
599
|
+
# Extended params with page
|
|
600
|
+
ext_params = {'page': page, **params}
|
|
601
|
+
|
|
591
602
|
# query the server
|
|
592
|
-
logger.info('Searching subtitles %r',
|
|
603
|
+
logger.info('Searching subtitles %r', ext_params)
|
|
593
604
|
|
|
594
605
|
# GET request and add page information
|
|
595
|
-
response = self.api_get('subtitles',
|
|
606
|
+
response = self.api_get('subtitles', ext_params)
|
|
596
607
|
|
|
597
608
|
if not response or not response['data']:
|
|
598
609
|
return []
|
|
599
610
|
|
|
600
611
|
ret = response['data']
|
|
601
612
|
|
|
613
|
+
# check that the maximum number of pages has not been exceeded
|
|
614
|
+
allow_more_pages = self.max_result_pages <= 0 or page < self.max_result_pages
|
|
615
|
+
|
|
602
616
|
# retrieve other pages maybe
|
|
603
|
-
if 'total_pages' in response and page < response['total_pages']:
|
|
617
|
+
if allow_more_pages and 'total_pages' in response and page < response['total_pages']:
|
|
604
618
|
# missing pages
|
|
605
619
|
ret.extend(self._search(page=page + 1, **params))
|
|
606
620
|
|
|
@@ -642,7 +656,8 @@ class OpenSubtitlesComProvider(Provider):
|
|
|
642
656
|
if query:
|
|
643
657
|
criterion.update({'query': query.replace("'", '')})
|
|
644
658
|
|
|
645
|
-
|
|
659
|
+
# Check for None to allow season=0 and episode=0
|
|
660
|
+
if season is not None and episode is not None:
|
|
646
661
|
criterion.update({'season_number': season, 'episode_number': episode})
|
|
647
662
|
if year:
|
|
648
663
|
criterion.update({'year': year})
|
|
@@ -312,7 +312,7 @@ if WITH_SYMPY: # pragma: no cover
|
|
|
312
312
|
"""
|
|
313
313
|
hash, title, year, country, release_group = symbols('hash title year country release_group') # noqa: A001
|
|
314
314
|
streaming_service, fps, source, audio_codec = symbols('streaming_service fps source audio_codec')
|
|
315
|
-
resolution, video_codec
|
|
315
|
+
resolution, video_codec = symbols('resolution video_codec')
|
|
316
316
|
|
|
317
317
|
equations = [
|
|
318
318
|
# hash is best
|