subliminal 2.3.2__tar.gz → 2.4.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.3.2 → subliminal-2.4.0}/.github/workflows/publish-release.yaml +2 -30
- subliminal-2.4.0/.github/zizmor.yml +13 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.pre-commit-config.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/HISTORY.rst +27 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/PKG-INFO +9 -2
- {subliminal-2.3.2 → subliminal-2.4.0}/README.rst +8 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/conf.py +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/user/configuration_file.rst +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/user/usage.rst +2 -2
- {subliminal-2.3.2 → subliminal-2.4.0}/pyproject.toml +2 -3
- subliminal-2.4.0/src/subliminal/cli/__init__.py +6 -0
- subliminal-2.4.0/src/subliminal/cli/cli.py +241 -0
- subliminal-2.4.0/src/subliminal/cli/commands/__init__.py +5 -0
- subliminal-2.4.0/src/subliminal/cli/commands/_format.py +63 -0
- subliminal-2.4.0/src/subliminal/cli/commands/download_best.py +632 -0
- subliminal-2.4.0/src/subliminal/cli/generate_config.py +131 -0
- subliminal-2.4.0/src/subliminal/cli/helpers.py +230 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/subtitulamos.py +1 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/refiners/omdb.py +23 -20
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/refiners/tvdb.py +8 -3
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/score.py +47 -52
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/utils.py +11 -45
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/video.py +2 -2
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_download_subtitle.yaml +5 -5
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_list_subtitles_episode.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_list_subtitles_movie.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_list_subtitles_movie_no_hash.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_login_bad_password.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_login_vip_bad_password.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_login_vip_login.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_no_operation.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_query_hash_size.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_query_imdb_id.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_query_not_enough_information.yaml +3 -3
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_query_query_episode.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_query_query_movie.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_query_query_season_episode.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitles/test_query_wrong_hash_wrong_size.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_download_subtitle.yaml +7 -7
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_episode.yaml +2 -2
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_movie.yaml +4 -4
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_movie_no_hash.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_login.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_login_bad_password.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_logout.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_query_hash_size.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_query_imdb_id.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_query_query_episode.yaml +2 -2
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_query_query_movie.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_query_query_season_episode.yaml +2 -2
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_query_tag_movie.yaml +1 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_tag_match.yaml +3 -3
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_user_infos.yaml +2 -2
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/providers/test_list_subtitles_providers_download.yaml +8 -8
- subliminal-2.4.0/tests/cli/test_cli.py +32 -0
- subliminal-2.3.2/tests/test_cli.py → subliminal-2.4.0/tests/cli/test_download.py +3 -66
- subliminal-2.4.0/tests/cli/test_generate_config.py +33 -0
- subliminal-2.4.0/tests/cli/test_helpers.py +95 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/conftest.py +9 -2
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_archives.py +2 -3
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_core.py +0 -1
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_utils.py +20 -66
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_video.py +15 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tox.ini +0 -8
- subliminal-2.3.2/.github/zizmor.yml +0 -4
- subliminal-2.3.2/scripts/generate-gh-release-notes.py +0 -86
- subliminal-2.3.2/src/subliminal/cli.py +0 -1169
- {subliminal-2.3.2 → subliminal-2.4.0}/.dockerignore +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.github/actions/prepare-tests/action.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.github/workflows/CI.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.github/workflows/coverage.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.github/workflows/docker-build.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.github/workflows/prepare-release-pr.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.github/workflows/publish.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.github/workflows/scheduled-tests.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.github/workflows/tag-release.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.gitignore +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.python-version-default +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/.readthedocs.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/CONTRIBUTING.md +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/Dockerfile +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/LICENSE +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/MANIFEST.in +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/RELEASING.md +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/changelog.d/.gitignore +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/Makefile +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/_intersphinx/README.md +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/_intersphinx/importlib_metadata_objects.inv +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/_intersphinx/importlib_metadata_objects.txt +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/_static/.gitignore +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/cache.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/cli.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/core.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/exceptions.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/extensions.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/providers.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/refiners.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/score.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/subtitle.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/utils.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/api/video.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/cassettes/test_usage.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/config.toml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/conftest.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/index.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/make.bat +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/user/changelog.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/user/cli.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/user/how_it_works.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/user/installation.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/docs/user/provider_guide.rst +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/hatch.toml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/scripts/.gitignore +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/scripts/prepare-release-pr.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/scripts/prepare_tests.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/scripts/release.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/__init__.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/__main__.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/archives.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/cache.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/converters/__init__.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/converters/addic7ed.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/converters/opensubtitles.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/converters/opensubtitlescom.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/converters/subtitulamos.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/converters/tvsubtitles.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/core.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/exceptions.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/extensions.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/matches.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/__init__.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/addic7ed.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/bsplayer.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/gestdown.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/mock.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/napiprojekt.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/opensubtitles.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/opensubtitlescom.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/podnapisi.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/providers/tvsubtitles.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/py.typed +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/refiners/__init__.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/refiners/hash.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/refiners/metadata.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/refiners/tmdb.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/src/subliminal/subtitle.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/__init__.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_download_subtitle.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_get_show_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_get_show_id_country.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_get_show_id_quote_dots_mixed_case.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_get_show_id_with_comma.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_get_show_id_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_get_show_ids.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_list_subtitles.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_list_subtitles_show_with_asterisk.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_query.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_query_no_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_query_parsing.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_query_parsing_colon.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_query_parsing_dash.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_query_parsing_quote_dots_mixed_case.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_query_wrong_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/addic7ed/test_query_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/bsplayer/test_download_subtitle.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/bsplayer/test_list_subtitles_hash.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/bsplayer/test_login.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/bsplayer/test_logout.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/bsplayer/test_query_hash_size.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/cli/test_cli_download.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_download_subtitle.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_download_with_bom.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_get_title_and_show_id_alternative_name.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_get_title_and_show_id_no_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_get_title_and_show_id_only_title.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_get_title_and_show_id_with_tvdb_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_list_subtitles.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_query.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_query_all_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_query_no_language.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_query_parsing.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_query_parsing_colon.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_query_parsing_dash.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_query_parsing_quote_dots_mixed_case.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_query_wrong_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_search_show_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_search_show_id_error.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_search_show_id_incomplete.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_search_show_id_no_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_search_show_id_quote.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_search_show_series_tvdb_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/gestdown/test_show_with_asterisk.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/napiprojekt/test_download_subtitles.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/napiprojekt/test_list_subtitles.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/napiprojekt/test_query_microdvd.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/napiprojekt/test_query_srt.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/napiprojekt/test_query_srt_reencode.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/napiprojekt/test_query_wrong_hash.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_get_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_get_title.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_get_wrong_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_get_wrong_title.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_refine_episode.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_refine_episode_original_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_refine_episode_with_country.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_refine_episode_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_refine_movie.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_refine_movie_guess_alternative_title.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_search.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_search_page.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_search_type.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_search_wrong_title.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/omdb/test_search_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/opensubtitlescom/test_query_wrong_hash_wrong_size.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/podnapisi/test_download_subtitle.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/podnapisi/test_download_subtitles_with_title_unicode.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/podnapisi/test_list_subtitles_episode.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/podnapisi/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/podnapisi/test_list_subtitles_movie.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/podnapisi/test_query_episode.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/podnapisi/test_query_movie.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/providers/test_download_best_subtitles.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/providers/test_download_best_subtitles_min_score.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/providers/test_download_best_subtitles_only_one.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/subtitulamos/test_download_subtitle.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/subtitulamos/test_download_subtitle_first_episode.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/subtitulamos/test_download_subtitle_foo.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/subtitulamos/test_download_subtitle_last_season.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/subtitulamos/test_download_subtitle_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_episode.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_language.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_season.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/subtitulamos/test_list_subtitles_with_spanish_non_mx_search.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_get_movie_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_get_movie_id_failed.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_get_series_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_get_series_wrong_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_refine_episode.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_refine_episode_original_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_refine_episode_with_country.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_refine_episode_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_refine_movie.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_refine_movie_guess_alternative_title.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_search_movie.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_search_movie_page.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_search_movie_wrong_title.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_search_movie_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_search_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tmdb/test_search_series_wrong_name.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_get_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_get_series_actors.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_get_series_actors_wrong_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_get_series_wrong_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_login.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_login_error.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_query_series_episodes.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_query_series_episodes_wrong_season.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refine.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refine_ambiguous.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refine_ambiguous_2.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refine_episode_alternative_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refine_episode_no_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refine_episode_partial.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refine_episode_with_comma.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refine_episode_with_country.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refine_episode_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_refresh_token.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_search_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_search_series_wrong_name.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvdb/test_token_needs_refresh.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_download_subtitle.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_download_subtitle_with_quote.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_get_episode_ids.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_get_episode_ids_wrong_season.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_list_subtitles.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_list_subtitles_episode_alternative_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_query.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_query_no_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_query_with_quote.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_query_wrong_episode.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_query_wrong_series.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_search_show_id.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_search_show_id_ambiguous.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_search_show_id_error.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_search_show_id_incomplete.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_search_show_id_no_year.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_search_show_id_uk.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_search_show_id_us.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/cassettes/tvsubtitles/test_search_show_id_year_in_title.yaml +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/providers/test_addic7ed.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/providers/test_bsplayer.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/providers/test_gestdown.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/providers/test_napiprojekt.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/providers/test_opensubtitles.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/providers/test_opensubtitlescom.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/providers/test_podnapisi.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/providers/test_providers.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/providers/test_subtitulamos.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/providers/test_tvsubtitles.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/refiners/test_hash.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/refiners/test_metadata.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/refiners/test_omdb.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/refiners/test_tmdb.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/refiners/test_tvdb.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_cache.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_extensions.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_matches.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_provider.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_provider_pool.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_score.py +0 -0
- {subliminal-2.3.2 → subliminal-2.4.0}/tests/test_subtitle.py +0 -0
|
@@ -35,7 +35,6 @@ jobs:
|
|
|
35
35
|
if: github.event_name == 'push'
|
|
36
36
|
with:
|
|
37
37
|
persist-credentials: false
|
|
38
|
-
|
|
39
38
|
- name: Checkout
|
|
40
39
|
uses: actions/checkout@v4
|
|
41
40
|
if: |
|
|
@@ -44,34 +43,13 @@ jobs:
|
|
|
44
43
|
with:
|
|
45
44
|
ref: ${{ inputs.tag-name }}
|
|
46
45
|
persist-credentials: false
|
|
47
|
-
|
|
48
46
|
- name: Build dist
|
|
49
47
|
uses: hynek/build-and-inspect-python-package@v2
|
|
50
48
|
id: baipp
|
|
51
|
-
|
|
52
|
-
- name: Install tox
|
|
53
|
-
run: |
|
|
54
|
-
python -m pip install --upgrade pip
|
|
55
|
-
pip install --upgrade tox
|
|
56
|
-
|
|
57
|
-
- name: Generate Release Notes
|
|
58
|
-
env:
|
|
59
|
-
RELEASE_VERSION: ${{ steps.baipp.outputs.package_version }}
|
|
60
|
-
run: |
|
|
61
|
-
sudo apt-get install pandoc
|
|
62
|
-
# use package_version computed in step 'baipp'
|
|
63
|
-
tox -e generate-gh-release-notes -- "$RELEASE_VERSION" latest-release-notes.md
|
|
64
|
-
|
|
65
|
-
- name: Upload release notes
|
|
66
|
-
uses: actions/upload-artifact@v4
|
|
67
|
-
with:
|
|
68
|
-
name: latest-release-notes
|
|
69
|
-
path: latest-release-notes.md
|
|
70
|
-
include-hidden-files: false
|
|
71
|
-
if-no-files-found: error
|
|
72
49
|
outputs:
|
|
73
50
|
tag: ${{ steps.baipp.outputs.package_version }}
|
|
74
51
|
|
|
52
|
+
|
|
75
53
|
github-release:
|
|
76
54
|
name: Make a GitHub Release
|
|
77
55
|
needs: [build-package]
|
|
@@ -96,15 +74,9 @@ jobs:
|
|
|
96
74
|
with:
|
|
97
75
|
name: Packages
|
|
98
76
|
path: dist
|
|
99
|
-
- name: Download release notes
|
|
100
|
-
uses: actions/download-artifact@v4
|
|
101
|
-
with:
|
|
102
|
-
name: latest-release-notes
|
|
103
|
-
path: latest-release-notes.md
|
|
104
|
-
|
|
105
77
|
- name: Publish GitHub Release
|
|
106
78
|
uses: softprops/action-gh-release@v2
|
|
107
79
|
with:
|
|
108
80
|
files: dist/*
|
|
109
|
-
|
|
81
|
+
generate_release_notes: true
|
|
110
82
|
draft: true
|
|
@@ -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.33.1
|
|
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.12.1
|
|
17
17
|
hooks:
|
|
18
18
|
- id: ruff
|
|
19
19
|
args: [--fix, --unsafe-fixes]
|
|
@@ -26,14 +26,14 @@ repos:
|
|
|
26
26
|
name: validate-pyproject
|
|
27
27
|
|
|
28
28
|
- repo: https://github.com/PyCQA/doc8
|
|
29
|
-
rev:
|
|
29
|
+
rev: v2.0.0
|
|
30
30
|
hooks:
|
|
31
31
|
- id: doc8
|
|
32
32
|
description: This hook runs doc8 for linting docs
|
|
33
33
|
require_serial: true
|
|
34
34
|
|
|
35
35
|
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
|
36
|
-
rev: v1.
|
|
36
|
+
rev: v1.10.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.4.0 (2025-09-03)
|
|
6
|
+
^^^^^^^^^^^^^^^^^^
|
|
7
|
+
|
|
8
|
+
Changes
|
|
9
|
+
-------
|
|
10
|
+
|
|
11
|
+
- score: add fps match, remove hearing_impaired match (`#1250 <https://github.com/Diaoul/subliminal/issues/1250>`__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
CLI Changes
|
|
15
|
+
-----------
|
|
16
|
+
|
|
17
|
+
- cli: refactor the cli.py file into a folder (`#1299 <https://github.com/Diaoul/subliminal/issues/1299>`__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Documentation
|
|
21
|
+
-------------
|
|
22
|
+
|
|
23
|
+
- doc: add a section in the README to run subliminal with the ``--debug`` flag. (`#1308 <https://github.com/Diaoul/subliminal/issues/1308>`__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
Misc
|
|
27
|
+
----
|
|
28
|
+
|
|
29
|
+
- ensure Episode.series is always a string, not a list from guessit bug (`#1304 <https://github.com/Diaoul/subliminal/issues/1304>`__)
|
|
30
|
+
|
|
31
|
+
|
|
5
32
|
2.3.2 (2025-05-08)
|
|
6
33
|
^^^^^^^^^^^^^^^^^^
|
|
7
34
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: subliminal
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.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
|
|
@@ -56,7 +56,6 @@ Provides-Extra: tests
|
|
|
56
56
|
Requires-Dist: colorama; extra == 'tests'
|
|
57
57
|
Requires-Dist: coverage[toml]>=7; extra == 'tests'
|
|
58
58
|
Requires-Dist: importlib-metadata>=4.6; (python_version < '3.10') and extra == 'tests'
|
|
59
|
-
Requires-Dist: pypandoc; extra == 'tests'
|
|
60
59
|
Requires-Dist: pytest-cov; extra == 'tests'
|
|
61
60
|
Requires-Dist: pytest-xdist; extra == 'tests'
|
|
62
61
|
Requires-Dist: pytest>=6.0; extra == 'tests'
|
|
@@ -159,6 +158,14 @@ Run subliminal in a docker container::
|
|
|
159
158
|
|
|
160
159
|
$ docker run --rm --name subliminal -v subliminal_cache:/usr/src/cache -v /tvshows:/tvshows -it ghcr.io/diaoul/subliminal download -l en /tvshows/The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.mp4
|
|
161
160
|
|
|
161
|
+
Debugging
|
|
162
|
+
^^^^^^^^^
|
|
163
|
+
|
|
164
|
+
By default, subliminal output is minimal. Run with the `--debug` flag before the `download` command to get more information::
|
|
165
|
+
|
|
166
|
+
$ subliminal --debug download -l en The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.mp4
|
|
167
|
+
|
|
168
|
+
|
|
162
169
|
Installation
|
|
163
170
|
------------
|
|
164
171
|
For a better isolation with your system you should use a dedicated virtualenv.
|
|
@@ -86,6 +86,14 @@ Run subliminal in a docker container::
|
|
|
86
86
|
|
|
87
87
|
$ docker run --rm --name subliminal -v subliminal_cache:/usr/src/cache -v /tvshows:/tvshows -it ghcr.io/diaoul/subliminal download -l en /tvshows/The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.mp4
|
|
88
88
|
|
|
89
|
+
Debugging
|
|
90
|
+
^^^^^^^^^
|
|
91
|
+
|
|
92
|
+
By default, subliminal output is minimal. Run with the `--debug` flag before the `download` command to get more information::
|
|
93
|
+
|
|
94
|
+
$ subliminal --debug download -l en The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.mp4
|
|
95
|
+
|
|
96
|
+
|
|
89
97
|
Installation
|
|
90
98
|
------------
|
|
91
99
|
For a better isolation with your system you should use a dedicated virtualenv.
|
|
@@ -304,7 +304,7 @@ intersphinx_mapping = {
|
|
|
304
304
|
'dogpilecache': ('https://dogpilecache.sqlalchemy.org/en/latest', None),
|
|
305
305
|
'stevedore': ('https://docs.openstack.org/stevedore/latest', None),
|
|
306
306
|
'click': ('https://click.palletsprojects.com/en/latest/', None),
|
|
307
|
-
'requests': ('https://
|
|
307
|
+
'requests': ('https://requests.readthedocs.io/en/latest/', None),
|
|
308
308
|
'sympy': ('https://docs.sympy.org/latest/', None),
|
|
309
309
|
'bs4': ('https://www.crummy.com/software/BeautifulSoup/bs4/doc/', None),
|
|
310
310
|
'importlib.metadata': ('https://docs.python.org/3/', '_intersphinx/importlib_metadata_objects.inv'),
|
|
@@ -40,5 +40,5 @@ Default configuration
|
|
|
40
40
|
Here is a list of all the options of the configuration file.
|
|
41
41
|
Commented options need a value to be valid.
|
|
42
42
|
|
|
43
|
-
.. program-output:: python -c "from subliminal.cli import generate_default_config; print(generate_default_config(commented=False))"
|
|
43
|
+
.. program-output:: python -c "from subliminal.cli.generate_config import generate_default_config; print(generate_default_config(commented=False))"
|
|
44
44
|
:language: toml
|
|
@@ -146,8 +146,8 @@ And then compute a score with those matches with :func:`~subliminal.score.comput
|
|
|
146
146
|
|
|
147
147
|
>>> for s in subtitles[video]:
|
|
148
148
|
... {s: compute_score(s, video)}
|
|
149
|
-
{<PodnapisiSubtitle 'ZtAW' [hu]>:
|
|
150
|
-
{<PodnapisiSubtitle 'ONAW' [hu]>:
|
|
149
|
+
{<PodnapisiSubtitle 'ZtAW' [hu]>: 941}
|
|
150
|
+
{<PodnapisiSubtitle 'ONAW' [hu]>: 922}
|
|
151
151
|
|
|
152
152
|
Now you should have a better idea about which one you should choose.
|
|
153
153
|
|
|
@@ -72,7 +72,6 @@ tests = [
|
|
|
72
72
|
"pytest-xdist",
|
|
73
73
|
"sympy",
|
|
74
74
|
"colorama", # used in scripts/
|
|
75
|
-
"pypandoc", # used in scripts/
|
|
76
75
|
"vcrpy>=5", # keep synchronized with docs dependencies
|
|
77
76
|
"win32-setctime; sys_platform=='win32'",
|
|
78
77
|
"importlib_metadata>=4.6; python_version<'3.10'",
|
|
@@ -275,12 +274,12 @@ showcontent = true
|
|
|
275
274
|
[[tool.towncrier.type]]
|
|
276
275
|
directory = "doc"
|
|
277
276
|
name = "Documentation"
|
|
278
|
-
showcontent =
|
|
277
|
+
showcontent = true
|
|
279
278
|
|
|
280
279
|
[[tool.towncrier.type]]
|
|
281
280
|
directory = "misc"
|
|
282
281
|
name = "Misc"
|
|
283
|
-
showcontent =
|
|
282
|
+
showcontent = true
|
|
284
283
|
|
|
285
284
|
|
|
286
285
|
# https://mypy.readthedocs.io/en/stable/config_file.html
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# ruff: noqa: FBT001
|
|
2
|
+
"""Subliminal uses `click <https://click.palletsprojects.com>`_ to provide a :abbr:`CLI (command-line interface)`."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import pathlib
|
|
9
|
+
import warnings
|
|
10
|
+
from datetime import timedelta
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
from platformdirs import PlatformDirs
|
|
16
|
+
|
|
17
|
+
from subliminal import (
|
|
18
|
+
__version__,
|
|
19
|
+
region,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from .commands import download
|
|
23
|
+
from .helpers import (
|
|
24
|
+
MutexLock,
|
|
25
|
+
options_from_providers,
|
|
26
|
+
options_from_refiners,
|
|
27
|
+
providers_config,
|
|
28
|
+
read_configuration,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def configure(ctx: click.Context, param: click.Parameter | None, filename: str | os.PathLike) -> None:
|
|
35
|
+
"""Update :class:`click.Context` based on a configuration file."""
|
|
36
|
+
config = read_configuration(filename)
|
|
37
|
+
|
|
38
|
+
ctx.obj = config['obj']
|
|
39
|
+
ctx.default_map = config['default_map']
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
dirs = PlatformDirs('subliminal')
|
|
43
|
+
cache_file = 'subliminal.dbm'
|
|
44
|
+
default_config_path = dirs.user_config_path / 'subliminal.toml'
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@click.group(
|
|
48
|
+
context_settings={'max_content_width': 100},
|
|
49
|
+
epilog='Suggestions and bug reports are greatly appreciated: https://github.com/Diaoul/subliminal/',
|
|
50
|
+
)
|
|
51
|
+
@click.option(
|
|
52
|
+
'-c',
|
|
53
|
+
'--config',
|
|
54
|
+
type=click.Path(dir_okay=False, path_type=pathlib.Path),
|
|
55
|
+
default=default_config_path,
|
|
56
|
+
callback=configure,
|
|
57
|
+
show_default=True,
|
|
58
|
+
is_eager=True,
|
|
59
|
+
expose_value=False,
|
|
60
|
+
show_envvar=True,
|
|
61
|
+
help='Path to the TOML configuration file.',
|
|
62
|
+
)
|
|
63
|
+
@click.option(
|
|
64
|
+
'--cache-dir',
|
|
65
|
+
type=click.Path(writable=True, file_okay=False),
|
|
66
|
+
default=dirs.user_cache_dir,
|
|
67
|
+
show_default=True,
|
|
68
|
+
expose_value=True,
|
|
69
|
+
help='Path to the cache directory.',
|
|
70
|
+
)
|
|
71
|
+
@providers_config.option(
|
|
72
|
+
'--addic7ed',
|
|
73
|
+
type=click.STRING,
|
|
74
|
+
nargs=2,
|
|
75
|
+
metavar='USERNAME PASSWORD',
|
|
76
|
+
help='DEPRECATED: Addic7ed configuration.',
|
|
77
|
+
)
|
|
78
|
+
@providers_config.option(
|
|
79
|
+
'--opensubtitles',
|
|
80
|
+
type=click.STRING,
|
|
81
|
+
nargs=2,
|
|
82
|
+
metavar='USERNAME PASSWORD',
|
|
83
|
+
help='DEPRECATED: OpenSubtitles configuration.',
|
|
84
|
+
)
|
|
85
|
+
@providers_config.option(
|
|
86
|
+
'--opensubtitlescom',
|
|
87
|
+
type=click.STRING,
|
|
88
|
+
nargs=2,
|
|
89
|
+
metavar='USERNAME PASSWORD',
|
|
90
|
+
help='DEPRECATED: OpenSubtitlesCom configuration.',
|
|
91
|
+
)
|
|
92
|
+
@options_from_providers
|
|
93
|
+
@options_from_refiners
|
|
94
|
+
@click.option('--debug', is_flag=True, help='Print useful information for debugging subliminal and for reporting bugs.')
|
|
95
|
+
@click.option(
|
|
96
|
+
'--logfile',
|
|
97
|
+
type=str,
|
|
98
|
+
default='',
|
|
99
|
+
help=(
|
|
100
|
+
'If defined, record information to the specified log file. '
|
|
101
|
+
'If the file already exists, new logs are appended to the file.'
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
@click.option(
|
|
105
|
+
'--logfile-level',
|
|
106
|
+
type=click.Choice(['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']),
|
|
107
|
+
default='DEBUG',
|
|
108
|
+
help='The logging level used for the log file.',
|
|
109
|
+
)
|
|
110
|
+
@click.version_option(__version__)
|
|
111
|
+
@click.pass_context
|
|
112
|
+
def subliminal(
|
|
113
|
+
ctx: click.Context,
|
|
114
|
+
/,
|
|
115
|
+
cache_dir: str,
|
|
116
|
+
debug: bool,
|
|
117
|
+
logfile: os.PathLike[str] | None,
|
|
118
|
+
logfile_level: str,
|
|
119
|
+
addic7ed: tuple[str, str],
|
|
120
|
+
opensubtitles: tuple[str, str],
|
|
121
|
+
opensubtitlescom: tuple[str, str],
|
|
122
|
+
**kwargs: Any,
|
|
123
|
+
) -> None:
|
|
124
|
+
"""Subtitles, faster than your thoughts."""
|
|
125
|
+
# make sure to expand user
|
|
126
|
+
cache_dir_path = Path(cache_dir).expanduser()
|
|
127
|
+
# create cache directory
|
|
128
|
+
try:
|
|
129
|
+
cache_dir_path.mkdir(parents=True)
|
|
130
|
+
except OSError: # pragma: no cover
|
|
131
|
+
if not cache_dir_path.is_dir():
|
|
132
|
+
raise
|
|
133
|
+
|
|
134
|
+
# configure cache
|
|
135
|
+
region.configure(
|
|
136
|
+
'dogpile.cache.dbm',
|
|
137
|
+
expiration_time=timedelta(days=30),
|
|
138
|
+
arguments={'filename': os.fspath(cache_dir_path / cache_file), 'lock_factory': MutexLock},
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Set the logger level to DEBUG in case debug or logfile is defined
|
|
142
|
+
subliminal_logger = logging.getLogger('subliminal')
|
|
143
|
+
subliminal_logger.setLevel(logging.DEBUG)
|
|
144
|
+
|
|
145
|
+
# configure logging file
|
|
146
|
+
if logfile:
|
|
147
|
+
# make sure to expand user
|
|
148
|
+
logfile_path = Path(logfile).expanduser()
|
|
149
|
+
try:
|
|
150
|
+
# make sure the parent directories exist
|
|
151
|
+
logfile_path.parent.mkdir(parents=True)
|
|
152
|
+
except OSError: # pragma: no cover
|
|
153
|
+
if not logfile_path.parent.is_dir():
|
|
154
|
+
raise
|
|
155
|
+
|
|
156
|
+
file_handler = logging.FileHandler(logfile_path)
|
|
157
|
+
file_handler.setFormatter(
|
|
158
|
+
logging.Formatter(
|
|
159
|
+
'%(asctime)s | %(levelname)-8s | %(name)s | %(message)s',
|
|
160
|
+
datefmt='%Y-%m-%d %H:%M:%S',
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
file_handler.setLevel(logfile_level)
|
|
164
|
+
subliminal_logger.addHandler(file_handler)
|
|
165
|
+
|
|
166
|
+
# configure logging
|
|
167
|
+
if debug:
|
|
168
|
+
handler = logging.StreamHandler()
|
|
169
|
+
handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
|
|
170
|
+
handler.setLevel(logging.DEBUG)
|
|
171
|
+
subliminal_logger.addHandler(handler)
|
|
172
|
+
# log about the config file
|
|
173
|
+
msg = ctx.obj['debug_message']
|
|
174
|
+
logger.info(msg)
|
|
175
|
+
|
|
176
|
+
ctx.obj['debug'] = debug
|
|
177
|
+
|
|
178
|
+
# create provider and refiner configs
|
|
179
|
+
provider_configs: dict[str, dict[str, Any]] = {}
|
|
180
|
+
refiner_configs: dict[str, dict[str, Any]] = {}
|
|
181
|
+
|
|
182
|
+
for k, v in kwargs.items():
|
|
183
|
+
try_split = k.split('__')
|
|
184
|
+
if len(try_split) != 3: # pragma: no cover
|
|
185
|
+
click.echo(f'Unknown option: {k}={v}')
|
|
186
|
+
continue
|
|
187
|
+
group, plugin, key = try_split
|
|
188
|
+
if group == '_provider':
|
|
189
|
+
provider_configs.setdefault(plugin, {})[key] = v
|
|
190
|
+
|
|
191
|
+
elif group == '_refiner': # pragma: no branch
|
|
192
|
+
refiner_configs.setdefault(plugin, {})[key] = v
|
|
193
|
+
|
|
194
|
+
ctx.obj['provider_configs'] = provider_configs
|
|
195
|
+
ctx.obj['refiner_configs'] = refiner_configs
|
|
196
|
+
|
|
197
|
+
# Deprecated options
|
|
198
|
+
# To be remove in next version
|
|
199
|
+
deprecated_options = {
|
|
200
|
+
'addic7ed': addic7ed,
|
|
201
|
+
'opensubtitles': opensubtitles,
|
|
202
|
+
'opensubtitlescom': opensubtitlescom,
|
|
203
|
+
}
|
|
204
|
+
for provider, option_value in deprecated_options.items():
|
|
205
|
+
if option_value is not None: # pragma: no cover
|
|
206
|
+
msg = (
|
|
207
|
+
f'option --{provider} is deprecated, use --provider.{provider}.username and '
|
|
208
|
+
f'--provider.{provider}.password'
|
|
209
|
+
)
|
|
210
|
+
warnings.warn(msg, DeprecationWarning, stacklevel=2)
|
|
211
|
+
|
|
212
|
+
provider_configs[provider]['username'] = option_value[0]
|
|
213
|
+
provider_configs[provider]['password'] = option_value[1]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@subliminal.command()
|
|
217
|
+
@click.option(
|
|
218
|
+
'--clear-subliminal',
|
|
219
|
+
is_flag=True,
|
|
220
|
+
help='Clear subliminal cache. Use this ONLY if your cache is corrupted or if you experience issues.',
|
|
221
|
+
)
|
|
222
|
+
@click.pass_context
|
|
223
|
+
def cache(ctx: click.Context, clear_subliminal: bool) -> None:
|
|
224
|
+
"""Cache management."""
|
|
225
|
+
if clear_subliminal and ctx.parent and 'cache_dir' in ctx.parent.params:
|
|
226
|
+
cache_dir_path = Path(ctx.parent.params['cache_dir'])
|
|
227
|
+
for file in (cache_dir_path / cache_file).glob('*'): # pragma: no cover
|
|
228
|
+
file.unlink()
|
|
229
|
+
click.echo("Subliminal's cache cleared.")
|
|
230
|
+
else:
|
|
231
|
+
click.echo('Nothing done.')
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# Add commands
|
|
235
|
+
# Download best subtitle
|
|
236
|
+
subliminal.add_command(download)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def cli() -> None: # pragma: no cover
|
|
240
|
+
"""CLI that recognizes environment variables."""
|
|
241
|
+
subliminal(auto_envvar_prefix='SUBLIMINAL')
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Helper functions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import re
|
|
7
|
+
from datetime import timedelta
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
from babelfish import Error as BabelfishError # type: ignore[import-untyped]
|
|
12
|
+
from babelfish import Language
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LanguageParamType(click.ParamType):
|
|
18
|
+
""":class:`~click.ParamType` for languages that returns a :class:`~babelfish.language.Language`."""
|
|
19
|
+
|
|
20
|
+
name = 'language'
|
|
21
|
+
|
|
22
|
+
def convert(self, value: str, param: click.Parameter | None, ctx: click.Context | None) -> Language:
|
|
23
|
+
"""Convert ietf language to :class:`~babelfish.language.Language`."""
|
|
24
|
+
try:
|
|
25
|
+
return Language.fromietf(value)
|
|
26
|
+
except BabelfishError:
|
|
27
|
+
self.fail(f'{value} is not a valid language', param, ctx) # pragma: no cover
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AgeParamType(click.ParamType):
|
|
31
|
+
""":class:`~click.ParamType` for age strings that returns a :class:`~datetime.timedelta`.
|
|
32
|
+
|
|
33
|
+
An age string is in the form `number + identifier` with possible identifiers:
|
|
34
|
+
|
|
35
|
+
* ``w`` for weeks
|
|
36
|
+
* ``d`` for days
|
|
37
|
+
* ``h`` for hours
|
|
38
|
+
|
|
39
|
+
The form can be specified multiple times but only with that identifier ordering. For example:
|
|
40
|
+
|
|
41
|
+
* ``1w2d4h`` for 1 week, 2 days and 4 hours
|
|
42
|
+
* ``2w`` for 2 weeks
|
|
43
|
+
* ``3w6h`` for 3 weeks and 6 hours
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
name = 'age'
|
|
47
|
+
|
|
48
|
+
def convert(self, value: str, param: click.Parameter | None, ctx: click.Context | None) -> timedelta:
|
|
49
|
+
"""Convert an age string to :class:`~datetime.timedelta`."""
|
|
50
|
+
match = re.match(r'^(?:(?P<weeks>\d+?)w)?(?:(?P<days>\d+?)d)?(?:(?P<hours>\d+?)h)?$', value)
|
|
51
|
+
if not match:
|
|
52
|
+
self.fail(f'{value} is not a valid age', param, ctx)
|
|
53
|
+
|
|
54
|
+
return timedelta(**{k: int(v) for k, v in match.groupdict(0).items()})
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def plural(quantity: int, name: str, *, bold: bool = True, **kwargs: Any) -> str:
|
|
58
|
+
"""Format a quantity with plural."""
|
|
59
|
+
return '{} {}{}'.format(
|
|
60
|
+
click.style(str(quantity), bold=bold, **kwargs),
|
|
61
|
+
name,
|
|
62
|
+
's' if quantity > 1 else '',
|
|
63
|
+
)
|