subliminal 2.4.0__tar.gz → 2.6.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.6.0}/.pre-commit-config.yaml +4 -4
- {subliminal-2.4.0 → subliminal-2.6.0}/HISTORY.rst +50 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/PKG-INFO +8 -3
- {subliminal-2.4.0 → subliminal-2.6.0}/README.rst +5 -1
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/providers.rst +5 -0
- subliminal-2.6.0/docs/assets/demo.gif +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/user/configuration_file.rst +5 -1
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/user/how_it_works.rst +1 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/hatch.toml +1 -1
- {subliminal-2.4.0 → subliminal-2.6.0}/pyproject.toml +3 -1
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/cli/generate_config.py +59 -28
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/core.py +2 -2
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/extensions.py +36 -11
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/__init__.py +1 -2
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/bsplayer.py +1 -1
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/napiprojekt.py +1 -2
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/opensubtitles.py +4 -3
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/opensubtitlescom.py +32 -22
- subliminal-2.6.0/src/subliminal/providers/subtis.py +336 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/subtitulamos.py +7 -3
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/refiners/hash.py +12 -8
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/refiners/tvdb.py +1 -23
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/score.py +1 -1
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/utils.py +4 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_get_id.yaml +15 -17
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_get_title.yaml +15 -17
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_get_wrong_id.yaml +11 -13
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_get_wrong_title.yaml +15 -17
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_refine_episode.yaml +11 -13
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_refine_episode_original_series.yaml +68 -80
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_refine_episode_with_country.yaml +17 -19
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_refine_episode_with_country_hoc_us.yaml +15 -18
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_refine_episode_year.yaml +11 -13
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_refine_movie.yaml +49 -54
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_refine_movie_guess_alternative_title.yaml +15 -16
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_search.yaml +11 -13
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_search_page.yaml +11 -13
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_search_type.yaml +11 -13
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_search_wrong_title.yaml +15 -17
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/omdb/test_search_year.yaml +11 -13
- subliminal-2.6.0/tests/cassettes/opensubtitlescom/test_query_max_result_pages.yaml +10748 -0
- subliminal-2.6.0/tests/cassettes/subtis/test_download_subtitle.yaml +7982 -0
- subliminal-2.6.0/tests/cassettes/subtis/test_list_subtitles_hash.yaml +258 -0
- subliminal-2.6.0/tests/cassettes/subtis/test_list_subtitles_movie.yaml +997 -0
- subliminal-2.6.0/tests/cassettes/subtis/test_list_subtitles_movie_alternative.yaml +682 -0
- subliminal-2.6.0/tests/cassettes/subtis/test_list_subtitles_movie_with_size.yaml +367 -0
- subliminal-2.6.0/tests/cassettes/subtis/test_list_subtitles_no_match.yaml +632 -0
- subliminal-2.6.0/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_series.yaml +110 -0
- subliminal-2.6.0/tests/cassettes/tmdb/test_search_movie_page.yaml +313 -0
- subliminal-2.6.0/tests/cli/test_cli.py +34 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cli/test_download.py +102 -110
- subliminal-2.6.0/tests/cli/test_generate_config.py +57 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/conftest.py +225 -6
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/providers/test_opensubtitlescom.py +15 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/providers/test_providers.py +26 -22
- subliminal-2.6.0/tests/providers/test_subtis.py +390 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/refiners/test_hash.py +1 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/refiners/test_omdb.py +13 -10
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/refiners/test_tmdb.py +8 -4
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/refiners/test_tvdb.py +0 -22
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_archives.py +4 -3
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_core.py +14 -3
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_provider_pool.py +19 -17
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_subtitle.py +1 -1
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_utils.py +1 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_video.py +2 -1
- subliminal-2.4.0/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_series.yaml +0 -114
- subliminal-2.4.0/tests/cassettes/tmdb/test_search_movie_page.yaml +0 -146
- subliminal-2.4.0/tests/cassettes/tvdb/test_refresh_token.yaml +0 -100
- subliminal-2.4.0/tests/cassettes/tvdb/test_token_needs_refresh.yaml +0 -50
- 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.6.0}/.dockerignore +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.github/actions/prepare-tests/action.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.github/workflows/CI.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.github/workflows/coverage.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.github/workflows/docker-build.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.github/workflows/prepare-release-pr.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.github/workflows/publish-release.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.github/workflows/publish.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.github/workflows/scheduled-tests.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.github/workflows/tag-release.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.github/zizmor.yml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.gitignore +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.python-version-default +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/.readthedocs.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/CONTRIBUTING.md +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/Dockerfile +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/LICENSE +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/MANIFEST.in +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/RELEASING.md +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/changelog.d/.gitignore +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/Makefile +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/_intersphinx/README.md +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/_intersphinx/importlib_metadata_objects.inv +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/_intersphinx/importlib_metadata_objects.txt +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/_static/.gitignore +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/cache.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/cli.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/core.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/exceptions.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/extensions.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/refiners.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/score.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/subtitle.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/utils.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/api/video.rst +0 -0
- {subliminal-2.4.0/docs → subliminal-2.6.0/docs/assets}/config.toml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/cassettes/test_usage.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/conf.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/conftest.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/index.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/make.bat +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/user/changelog.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/user/cli.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/user/installation.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/user/provider_guide.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/docs/user/usage.rst +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/scripts/.gitignore +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/scripts/prepare-release-pr.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/scripts/prepare_tests.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/scripts/release.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/__main__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/archives.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/cache.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/cli/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/cli/cli.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/cli/commands/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/cli/commands/_format.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/cli/commands/download_best.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/cli/helpers.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/converters/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/converters/addic7ed.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/converters/opensubtitles.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/converters/opensubtitlescom.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/converters/subtitulamos.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/converters/tvsubtitles.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/exceptions.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/matches.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/addic7ed.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/gestdown.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/mock.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/podnapisi.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/providers/tvsubtitles.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/py.typed +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/refiners/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/refiners/metadata.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/refiners/omdb.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/refiners/tmdb.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/subtitle.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/src/subliminal/video.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/__init__.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_get_show_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_get_show_id_country.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_get_show_id_quote_dots_mixed_case.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_get_show_id_with_comma.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_get_show_id_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_get_show_ids.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_list_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_list_subtitles_show_with_asterisk.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_query.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_query_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_query_parsing.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_query_parsing_colon.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_query_parsing_dash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_query_parsing_quote_dots_mixed_case.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_query_wrong_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/addic7ed/test_query_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/bsplayer/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/bsplayer/test_list_subtitles_hash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/bsplayer/test_login.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/bsplayer/test_logout.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/bsplayer/test_query_hash_size.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/cli/test_cli_download.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_download_with_bom.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_get_title_and_show_id_alternative_name.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_get_title_and_show_id_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_get_title_and_show_id_only_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_get_title_and_show_id_with_tvdb_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_list_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_query.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_query_all_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_query_no_language.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_query_parsing.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_query_parsing_colon.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_query_parsing_dash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_query_parsing_quote_dots_mixed_case.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_query_wrong_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_search_show_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_search_show_id_error.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_search_show_id_incomplete.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_search_show_id_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_search_show_id_quote.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_search_show_series_tvdb_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/gestdown/test_show_with_asterisk.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/napiprojekt/test_download_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/napiprojekt/test_list_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/napiprojekt/test_query_microdvd.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/napiprojekt/test_query_srt.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/napiprojekt/test_query_srt_reencode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/napiprojekt/test_query_wrong_hash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_list_subtitles_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_list_subtitles_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_list_subtitles_movie_no_hash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_login_bad_password.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_login_vip_bad_password.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_login_vip_login.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_no_operation.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_query_hash_size.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_query_imdb_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_query_not_enough_information.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_query_query_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_query_query_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_query_query_season_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitles/test_query_wrong_hash_wrong_size.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_movie_no_hash.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_login.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_login_bad_password.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_logout.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_query_hash_size.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_query_imdb_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_query_query_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_query_query_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_query_query_season_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_query_tag_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_query_wrong_hash_wrong_size.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_tag_match.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/opensubtitlescom/test_user_infos.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/podnapisi/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/podnapisi/test_download_subtitles_with_title_unicode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/podnapisi/test_list_subtitles_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/podnapisi/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/podnapisi/test_list_subtitles_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/podnapisi/test_query_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/podnapisi/test_query_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/providers/test_download_best_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/providers/test_download_best_subtitles_min_score.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/providers/test_download_best_subtitles_only_one.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/providers/test_list_subtitles_providers_download.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/subtitulamos/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/subtitulamos/test_download_subtitle_first_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/subtitulamos/test_download_subtitle_foo.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/subtitulamos/test_download_subtitle_last_season.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/subtitulamos/test_download_subtitle_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_language.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_season.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/subtitulamos/test_list_subtitles_with_spanish_non_mx_search.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_get_movie_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_get_movie_id_failed.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_get_series_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_get_series_wrong_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_refine_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_refine_episode_original_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_refine_episode_with_country.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_refine_episode_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_refine_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_refine_movie_guess_alternative_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_search_movie.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_search_movie_wrong_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_search_movie_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_search_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tmdb/test_search_series_wrong_name.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_get_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_get_series_actors.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_get_series_actors_wrong_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_get_series_wrong_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_login.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_login_error.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_query_series_episodes.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_query_series_episodes_wrong_season.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_refine.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_refine_ambiguous.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_refine_ambiguous_2.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_refine_episode_alternative_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_refine_episode_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_refine_episode_partial.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_refine_episode_with_comma.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_refine_episode_with_country.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_refine_episode_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_search_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvdb/test_search_series_wrong_name.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_download_subtitle.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_download_subtitle_with_quote.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_get_episode_ids.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_get_episode_ids_wrong_season.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_list_subtitles.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_query.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_query_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_query_with_quote.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_query_wrong_episode.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_query_wrong_series.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_search_show_id.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_search_show_id_ambiguous.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_search_show_id_error.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_search_show_id_incomplete.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_search_show_id_no_year.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_search_show_id_uk.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_search_show_id_us.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cassettes/tvsubtitles/test_search_show_id_year_in_title.yaml +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/cli/test_helpers.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/providers/test_addic7ed.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/providers/test_bsplayer.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/providers/test_gestdown.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/providers/test_napiprojekt.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/providers/test_opensubtitles.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/providers/test_podnapisi.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/providers/test_subtitulamos.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/providers/test_tvsubtitles.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/refiners/test_metadata.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_cache.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_extensions.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_matches.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_provider.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.0}/tests/test_score.py +0 -0
- {subliminal-2.4.0 → subliminal-2.6.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,56 @@
|
|
|
2
2
|
|
|
3
3
|
.. towncrier release notes start
|
|
4
4
|
|
|
5
|
+
2.6.0 (2026-02-20)
|
|
6
|
+
^^^^^^^^^^^^^^^^^^
|
|
7
|
+
|
|
8
|
+
Provider Changes
|
|
9
|
+
----------------
|
|
10
|
+
|
|
11
|
+
- OpenSubtitlesCom: replace recursive ``_search()`` implementation to prevent a stack overflow in some edge case (`#1336 <https://github.com/Diaoul/subliminal/issues/1336>`__)
|
|
12
|
+
- Add Subtis provider for Spanish subtitles (`#1337 <https://github.com/Diaoul/subliminal/issues/1337>`__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Refiner Changes
|
|
16
|
+
---------------
|
|
17
|
+
|
|
18
|
+
- hash: cache the result of hash_opensubtitles so it can be reused costlessly (`#1340 <https://github.com/Diaoul/subliminal/issues/1340>`__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Misc
|
|
22
|
+
----
|
|
23
|
+
|
|
24
|
+
- Fixes for some scheduled tests failures (`#1339 <https://github.com/Diaoul/subliminal/issues/1339>`__)
|
|
25
|
+
- add a gif to show CLI usage (`#1341 <https://github.com/Diaoul/subliminal/issues/1341>`__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
2.5.0 (2025-12-14)
|
|
29
|
+
^^^^^^^^^^^^^^^^^^
|
|
30
|
+
|
|
31
|
+
Provider Changes
|
|
32
|
+
----------------
|
|
33
|
+
|
|
34
|
+
- 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>`__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
Documentation
|
|
38
|
+
-------------
|
|
39
|
+
|
|
40
|
+
- 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>`__)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
Misc
|
|
44
|
+
----
|
|
45
|
+
|
|
46
|
+
- add support for python 3.14 (`#1317 <https://github.com/Diaoul/subliminal/issues/1317>`__)
|
|
47
|
+
- Fix test with click 8.3.0 running in parallel (`#1320 <https://github.com/Diaoul/subliminal/issues/1320>`__)
|
|
48
|
+
- fix test on big-endian system (`#1323 <https://github.com/Diaoul/subliminal/issues/1323>`__)
|
|
49
|
+
- compatibility with stevedore 5.6.0 (`#1325 <https://github.com/Diaoul/subliminal/issues/1325>`__)
|
|
50
|
+
- fix some tests to pass in a dirty environment (`#1326 <https://github.com/Diaoul/subliminal/issues/1326>`__)
|
|
51
|
+
- update pre-commit hook versions (`#1329 <https://github.com/Diaoul/subliminal/issues/1329>`__)
|
|
52
|
+
- Fix some test so they are more reliable and stable (`#1331 <https://github.com/Diaoul/subliminal/issues/1331>`__)
|
|
53
|
+
|
|
54
|
+
|
|
5
55
|
2.4.0 (2025-09-03)
|
|
6
56
|
^^^^^^^^^^^^^^^^^^
|
|
7
57
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: subliminal
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.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
|
|
@@ -102,6 +103,10 @@ Subtitles, faster than your thoughts.
|
|
|
102
103
|
|
|
103
104
|
Usage
|
|
104
105
|
-----
|
|
106
|
+
|
|
107
|
+
.. image:: https://github.com/Diaoul/subliminal/blob/main/docs/assets/demo.gif
|
|
108
|
+
:alt: Demo of the app CLI usage
|
|
109
|
+
|
|
105
110
|
CLI
|
|
106
111
|
^^^
|
|
107
112
|
Download English subtitles::
|
|
@@ -119,7 +124,7 @@ By default it looks for a `subliminal.toml` file in the default configuration fo
|
|
|
119
124
|
(see the CLI help for the exact platform-specific default path).
|
|
120
125
|
Or use the `-c` option to specify the path to the configuration file.
|
|
121
126
|
|
|
122
|
-
`Look for this example configuration file <https://github.com/Diaoul/subliminal/blob/main/docs/config.toml>`__
|
|
127
|
+
`Look for this example configuration file <https://github.com/Diaoul/subliminal/blob/main/docs/assets/config.toml>`__
|
|
123
128
|
or use the `generate_default_config` function from the `subliminal.cli` module to generate a
|
|
124
129
|
configuration file with all the options and their default values::
|
|
125
130
|
|
|
@@ -30,6 +30,10 @@ Subtitles, faster than your thoughts.
|
|
|
30
30
|
|
|
31
31
|
Usage
|
|
32
32
|
-----
|
|
33
|
+
|
|
34
|
+
.. image:: https://github.com/Diaoul/subliminal/blob/main/docs/assets/demo.gif
|
|
35
|
+
:alt: Demo of the app CLI usage
|
|
36
|
+
|
|
33
37
|
CLI
|
|
34
38
|
^^^
|
|
35
39
|
Download English subtitles::
|
|
@@ -47,7 +51,7 @@ By default it looks for a `subliminal.toml` file in the default configuration fo
|
|
|
47
51
|
(see the CLI help for the exact platform-specific default path).
|
|
48
52
|
Or use the `-c` option to specify the path to the configuration file.
|
|
49
53
|
|
|
50
|
-
`Look for this example configuration file <https://github.com/Diaoul/subliminal/blob/main/docs/config.toml>`__
|
|
54
|
+
`Look for this example configuration file <https://github.com/Diaoul/subliminal/blob/main/docs/assets/config.toml>`__
|
|
51
55
|
or use the `generate_default_config` function from the `subliminal.cli` module to generate a
|
|
52
56
|
configuration file with all the options and their default values::
|
|
53
57
|
|
|
Binary file
|
|
@@ -24,13 +24,17 @@ 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
|
-------
|
|
30
34
|
|
|
31
35
|
An example of configuration file:
|
|
32
36
|
|
|
33
|
-
.. literalinclude:: ../config.toml
|
|
37
|
+
.. literalinclude:: ../assets/config.toml
|
|
34
38
|
:language: toml
|
|
35
39
|
|
|
36
40
|
|
|
@@ -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",
|
|
@@ -105,6 +106,7 @@ opensubtitlesvip = "subliminal.providers.opensubtitles:OpenSubtitlesVipProvider"
|
|
|
105
106
|
opensubtitlescom = "subliminal.providers.opensubtitlescom:OpenSubtitlesComProvider"
|
|
106
107
|
opensubtitlescomvip = "subliminal.providers.opensubtitlescom:OpenSubtitlesComVipProvider"
|
|
107
108
|
podnapisi = "subliminal.providers.podnapisi:PodnapisiProvider"
|
|
109
|
+
subtis = "subliminal.providers.subtis:SubtisProvider"
|
|
108
110
|
subtitulamos = "subliminal.providers.subtitulamos:SubtitulamosProvider"
|
|
109
111
|
tvsubtitles = "subliminal.providers.tvsubtitles:TVsubtitlesProvider"
|
|
110
112
|
|
|
@@ -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
|
|
@@ -133,6 +157,7 @@ provider_manager = RegistrableExtensionManager(
|
|
|
133
157
|
'opensubtitlescomvip = subliminal.providers.opensubtitlescom:OpenSubtitlesComVipProvider',
|
|
134
158
|
'opensubtitlesvip = subliminal.providers.opensubtitles:OpenSubtitlesVipProvider',
|
|
135
159
|
'podnapisi = subliminal.providers.podnapisi:PodnapisiProvider',
|
|
160
|
+
'subtis = subliminal.providers.subtis:SubtisProvider',
|
|
136
161
|
'subtitulamos = subliminal.providers.subtitulamos:SubtitulamosProvider',
|
|
137
162
|
'tvsubtitles = subliminal.providers.tvsubtitles:TVsubtitlesProvider',
|
|
138
163
|
],
|
|
@@ -24,7 +24,6 @@ from subliminal.subtitle import Subtitle
|
|
|
24
24
|
from subliminal.video import Episode, Movie, Video
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
-
import os
|
|
28
27
|
from collections.abc import Sequence, Set
|
|
29
28
|
from http.client import HTTPSConnection
|
|
30
29
|
from types import TracebackType
|
|
@@ -146,7 +145,7 @@ class Provider(Generic[S]):
|
|
|
146
145
|
user_agent: str = f'Subliminal/{__short_version__}'
|
|
147
146
|
|
|
148
147
|
@staticmethod
|
|
149
|
-
def hash_video(video_path: str
|
|
148
|
+
def hash_video(video_path: str) -> str | None:
|
|
150
149
|
"""Hash the video to be used by the provider."""
|
|
151
150
|
return None
|
|
152
151
|
|
|
@@ -200,7 +200,7 @@ class BSPlayerProvider(Provider):
|
|
|
200
200
|
self.search_url = search_url or get_sub_domain()
|
|
201
201
|
|
|
202
202
|
@staticmethod
|
|
203
|
-
def hash_video(video_path: str
|
|
203
|
+
def hash_video(video_path: str) -> str | None:
|
|
204
204
|
"""Compute a hash using BSPlayer algorithm.
|
|
205
205
|
|
|
206
206
|
:param str video_path: path of the video.
|
|
@@ -17,7 +17,6 @@ from subliminal.subtitle import Subtitle, fix_line_ending
|
|
|
17
17
|
from . import Provider
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
20
|
-
import os
|
|
21
20
|
from collections.abc import Set
|
|
22
21
|
|
|
23
22
|
from subliminal.video import Video
|
|
@@ -98,7 +97,7 @@ class NapiProjektProvider(Provider):
|
|
|
98
97
|
self.session = None
|
|
99
98
|
|
|
100
99
|
@staticmethod
|
|
101
|
-
def hash_video(video_path: str
|
|
100
|
+
def hash_video(video_path: str) -> str | None:
|
|
102
101
|
"""Compute a hash using NapiProjekt's algorithm.
|
|
103
102
|
|
|
104
103
|
:param str video_path: path of the video.
|
|
@@ -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
|
|
|
@@ -279,7 +280,7 @@ class OpenSubtitlesProvider(Provider):
|
|
|
279
280
|
language = Language.fromopensubtitles(subtitle_item['SubLanguageID'])
|
|
280
281
|
hearing_impaired = bool(int(subtitle_item['SubHearingImpaired']))
|
|
281
282
|
page_link = subtitle_item['SubtitlesLink']
|
|
282
|
-
subtitle_id = int(subtitle_item['IDSubtitleFile'])
|
|
283
|
+
subtitle_id = str(int(subtitle_item['IDSubtitleFile']))
|
|
283
284
|
matched_by = subtitle_item['MatchedBy']
|
|
284
285
|
movie_kind = subtitle_item['MovieKind']
|
|
285
286
|
moviehash = subtitle_item['MovieHash']
|
|
@@ -31,7 +31,7 @@ from subliminal.video import Episode, Movie, Video
|
|
|
31
31
|
from . import Provider
|
|
32
32
|
|
|
33
33
|
if TYPE_CHECKING:
|
|
34
|
-
from collections.abc import Mapping, Set
|
|
34
|
+
from collections.abc import Generator, Mapping, Set
|
|
35
35
|
|
|
36
36
|
C = TypeVar('C', bound=Callable)
|
|
37
37
|
|
|
@@ -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
|
|
@@ -587,24 +595,28 @@ class OpenSubtitlesComProvider(Provider):
|
|
|
587
595
|
|
|
588
596
|
return r.json() # type: ignore[no-any-return]
|
|
589
597
|
|
|
590
|
-
def _search(self,
|
|
591
|
-
|
|
592
|
-
logger.info('Searching subtitles %r', params)
|
|
598
|
+
def _search(self, **params: Any) -> Generator[dict[str, Any], None, None]:
|
|
599
|
+
page = 1
|
|
593
600
|
|
|
594
|
-
|
|
595
|
-
|
|
601
|
+
while True:
|
|
602
|
+
# Extended params with page
|
|
603
|
+
ext_params = {'page': page, **params}
|
|
604
|
+
logger.info('Searching subtitles %r', ext_params)
|
|
605
|
+
# GET request and add page information
|
|
606
|
+
response = self.api_get('subtitles', ext_params)
|
|
607
|
+
if not response or not response['data']:
|
|
608
|
+
break
|
|
609
|
+
yield from response['data']
|
|
596
610
|
|
|
597
|
-
|
|
598
|
-
return []
|
|
611
|
+
page += 1
|
|
599
612
|
|
|
600
|
-
|
|
613
|
+
# check that the maximum number of pages has not been exceeded
|
|
614
|
+
if self.max_result_pages > 0 and page > self.max_result_pages:
|
|
615
|
+
break
|
|
601
616
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
ret.extend(self._search(page=page + 1, **params))
|
|
606
|
-
|
|
607
|
-
return ret # type: ignore[no-any-return]
|
|
617
|
+
# check if we fetched all pages already
|
|
618
|
+
if 'total_pages' not in response or page > response['total_pages']:
|
|
619
|
+
break
|
|
608
620
|
|
|
609
621
|
def _make_query(
|
|
610
622
|
self,
|
|
@@ -642,7 +654,8 @@ class OpenSubtitlesComProvider(Provider):
|
|
|
642
654
|
if query:
|
|
643
655
|
criterion.update({'query': query.replace("'", '')})
|
|
644
656
|
|
|
645
|
-
|
|
657
|
+
# Check for None to allow season=0 and episode=0
|
|
658
|
+
if season is not None and episode is not None:
|
|
646
659
|
criterion.update({'season_number': season, 'episode_number': episode})
|
|
647
660
|
if year:
|
|
648
661
|
criterion.update({'year': year})
|
|
@@ -696,14 +709,11 @@ class OpenSubtitlesComProvider(Provider):
|
|
|
696
709
|
# add the language and query the server
|
|
697
710
|
criterion.update({'languages': ','.join(sorted(lang.opensubtitlescom for lang in languages))})
|
|
698
711
|
|
|
699
|
-
# query the server
|
|
700
|
-
responses = self._search(**criterion)
|
|
701
|
-
|
|
702
712
|
imdb_match = 'imdb_id' in criterion or 'show_imdb_id' in criterion
|
|
703
713
|
tmdb_match = 'tmdb_id' in criterion or 'show_tmdb_id' in criterion
|
|
704
714
|
|
|
705
715
|
# loop over subtitle items
|
|
706
|
-
for response in
|
|
716
|
+
for response in self._search(**criterion):
|
|
707
717
|
# read single response
|
|
708
718
|
subtitle = self.subtitle_class.from_response(
|
|
709
719
|
response,
|