subliminal 2.3.2__tar.gz → 2.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. {subliminal-2.3.2 → subliminal-2.5.0}/.github/workflows/publish-release.yaml +2 -30
  2. subliminal-2.5.0/.github/zizmor.yml +13 -0
  3. {subliminal-2.3.2 → subliminal-2.5.0}/.pre-commit-config.yaml +5 -5
  4. {subliminal-2.3.2 → subliminal-2.5.0}/HISTORY.rst +54 -0
  5. {subliminal-2.3.2 → subliminal-2.5.0}/PKG-INFO +11 -3
  6. {subliminal-2.3.2 → subliminal-2.5.0}/README.rst +8 -0
  7. {subliminal-2.3.2 → subliminal-2.5.0}/docs/conf.py +1 -1
  8. {subliminal-2.3.2 → subliminal-2.5.0}/docs/user/configuration_file.rst +5 -1
  9. {subliminal-2.3.2 → subliminal-2.5.0}/docs/user/usage.rst +2 -2
  10. {subliminal-2.3.2 → subliminal-2.5.0}/hatch.toml +1 -1
  11. {subliminal-2.3.2 → subliminal-2.5.0}/pyproject.toml +4 -4
  12. subliminal-2.5.0/src/subliminal/cli/__init__.py +6 -0
  13. subliminal-2.5.0/src/subliminal/cli/cli.py +241 -0
  14. subliminal-2.5.0/src/subliminal/cli/commands/__init__.py +5 -0
  15. subliminal-2.5.0/src/subliminal/cli/commands/_format.py +63 -0
  16. subliminal-2.5.0/src/subliminal/cli/commands/download_best.py +632 -0
  17. subliminal-2.5.0/src/subliminal/cli/generate_config.py +162 -0
  18. subliminal-2.5.0/src/subliminal/cli/helpers.py +230 -0
  19. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/core.py +2 -2
  20. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/extensions.py +35 -11
  21. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/opensubtitles.py +5 -3
  22. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/opensubtitlescom.py +21 -6
  23. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/subtitulamos.py +1 -0
  24. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/refiners/omdb.py +23 -20
  25. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/refiners/tvdb.py +8 -3
  26. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/score.py +47 -52
  27. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/utils.py +11 -45
  28. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/video.py +2 -2
  29. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_download_subtitle.yaml +5 -5
  30. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_list_subtitles_episode.yaml +4 -4
  31. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_list_subtitles_movie.yaml +4 -4
  32. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_list_subtitles_movie_no_hash.yaml +4 -4
  33. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_login_bad_password.yaml +1 -1
  34. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_login_vip_bad_password.yaml +1 -1
  35. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_login_vip_login.yaml +1 -1
  36. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_no_operation.yaml +4 -4
  37. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_hash_size.yaml +4 -4
  38. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_imdb_id.yaml +4 -4
  39. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_not_enough_information.yaml +3 -3
  40. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_query_episode.yaml +4 -4
  41. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_query_movie.yaml +4 -4
  42. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_query_season_episode.yaml +4 -4
  43. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitles/test_query_wrong_hash_wrong_size.yaml +4 -4
  44. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_download_subtitle.yaml +7 -7
  45. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_episode.yaml +2 -2
  46. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_movie.yaml +4 -4
  47. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_list_subtitles_movie_no_hash.yaml +1 -1
  48. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_login.yaml +1 -1
  49. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_login_bad_password.yaml +1 -1
  50. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_logout.yaml +1 -1
  51. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_hash_size.yaml +1 -1
  52. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_imdb_id.yaml +1 -1
  53. subliminal-2.5.0/tests/cassettes/opensubtitlescom/test_query_max_result_pages.yaml +10748 -0
  54. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_query_episode.yaml +2 -2
  55. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_query_movie.yaml +1 -1
  56. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_query_season_episode.yaml +2 -2
  57. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_tag_movie.yaml +1 -1
  58. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_tag_match.yaml +3 -3
  59. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_user_infos.yaml +2 -2
  60. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/providers/test_list_subtitles_providers_download.yaml +8 -8
  61. subliminal-2.5.0/tests/cassettes/tmdb/test_search_movie_page.yaml +313 -0
  62. subliminal-2.5.0/tests/cli/test_cli.py +34 -0
  63. subliminal-2.3.2/tests/test_cli.py → subliminal-2.5.0/tests/cli/test_download.py +102 -173
  64. subliminal-2.5.0/tests/cli/test_generate_config.py +57 -0
  65. subliminal-2.5.0/tests/cli/test_helpers.py +95 -0
  66. {subliminal-2.3.2 → subliminal-2.5.0}/tests/conftest.py +232 -6
  67. {subliminal-2.3.2 → subliminal-2.5.0}/tests/providers/test_opensubtitlescom.py +15 -0
  68. {subliminal-2.3.2 → subliminal-2.5.0}/tests/providers/test_providers.py +23 -19
  69. {subliminal-2.3.2 → subliminal-2.5.0}/tests/refiners/test_tmdb.py +8 -4
  70. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_archives.py +6 -6
  71. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_core.py +14 -4
  72. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_provider_pool.py +19 -17
  73. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_subtitle.py +1 -1
  74. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_utils.py +20 -66
  75. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_video.py +17 -1
  76. {subliminal-2.3.2 → subliminal-2.5.0}/tox.ini +0 -8
  77. subliminal-2.3.2/.github/zizmor.yml +0 -4
  78. subliminal-2.3.2/scripts/generate-gh-release-notes.py +0 -86
  79. subliminal-2.3.2/src/subliminal/cli.py +0 -1169
  80. subliminal-2.3.2/tests/cassettes/tmdb/test_search_movie_page.yaml +0 -146
  81. {subliminal-2.3.2 → subliminal-2.5.0}/.dockerignore +0 -0
  82. {subliminal-2.3.2 → subliminal-2.5.0}/.github/actions/prepare-tests/action.yaml +0 -0
  83. {subliminal-2.3.2 → subliminal-2.5.0}/.github/workflows/CI.yaml +0 -0
  84. {subliminal-2.3.2 → subliminal-2.5.0}/.github/workflows/coverage.yaml +0 -0
  85. {subliminal-2.3.2 → subliminal-2.5.0}/.github/workflows/docker-build.yaml +0 -0
  86. {subliminal-2.3.2 → subliminal-2.5.0}/.github/workflows/prepare-release-pr.yaml +0 -0
  87. {subliminal-2.3.2 → subliminal-2.5.0}/.github/workflows/publish.yaml +0 -0
  88. {subliminal-2.3.2 → subliminal-2.5.0}/.github/workflows/scheduled-tests.yaml +0 -0
  89. {subliminal-2.3.2 → subliminal-2.5.0}/.github/workflows/tag-release.yaml +0 -0
  90. {subliminal-2.3.2 → subliminal-2.5.0}/.gitignore +0 -0
  91. {subliminal-2.3.2 → subliminal-2.5.0}/.python-version-default +0 -0
  92. {subliminal-2.3.2 → subliminal-2.5.0}/.readthedocs.yaml +0 -0
  93. {subliminal-2.3.2 → subliminal-2.5.0}/CONTRIBUTING.md +0 -0
  94. {subliminal-2.3.2 → subliminal-2.5.0}/Dockerfile +0 -0
  95. {subliminal-2.3.2 → subliminal-2.5.0}/LICENSE +0 -0
  96. {subliminal-2.3.2 → subliminal-2.5.0}/MANIFEST.in +0 -0
  97. {subliminal-2.3.2 → subliminal-2.5.0}/RELEASING.md +0 -0
  98. {subliminal-2.3.2 → subliminal-2.5.0}/changelog.d/.gitignore +0 -0
  99. {subliminal-2.3.2 → subliminal-2.5.0}/docs/Makefile +0 -0
  100. {subliminal-2.3.2 → subliminal-2.5.0}/docs/_intersphinx/README.md +0 -0
  101. {subliminal-2.3.2 → subliminal-2.5.0}/docs/_intersphinx/importlib_metadata_objects.inv +0 -0
  102. {subliminal-2.3.2 → subliminal-2.5.0}/docs/_intersphinx/importlib_metadata_objects.txt +0 -0
  103. {subliminal-2.3.2 → subliminal-2.5.0}/docs/_static/.gitignore +0 -0
  104. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/cache.rst +0 -0
  105. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/cli.rst +0 -0
  106. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/core.rst +0 -0
  107. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/exceptions.rst +0 -0
  108. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/extensions.rst +0 -0
  109. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/providers.rst +0 -0
  110. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/refiners.rst +0 -0
  111. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/score.rst +0 -0
  112. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/subtitle.rst +0 -0
  113. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/utils.rst +0 -0
  114. {subliminal-2.3.2 → subliminal-2.5.0}/docs/api/video.rst +0 -0
  115. {subliminal-2.3.2 → subliminal-2.5.0}/docs/cassettes/test_usage.yaml +0 -0
  116. {subliminal-2.3.2 → subliminal-2.5.0}/docs/config.toml +0 -0
  117. {subliminal-2.3.2 → subliminal-2.5.0}/docs/conftest.py +0 -0
  118. {subliminal-2.3.2 → subliminal-2.5.0}/docs/index.rst +0 -0
  119. {subliminal-2.3.2 → subliminal-2.5.0}/docs/make.bat +0 -0
  120. {subliminal-2.3.2 → subliminal-2.5.0}/docs/user/changelog.rst +0 -0
  121. {subliminal-2.3.2 → subliminal-2.5.0}/docs/user/cli.rst +0 -0
  122. {subliminal-2.3.2 → subliminal-2.5.0}/docs/user/how_it_works.rst +0 -0
  123. {subliminal-2.3.2 → subliminal-2.5.0}/docs/user/installation.rst +0 -0
  124. {subliminal-2.3.2 → subliminal-2.5.0}/docs/user/provider_guide.rst +0 -0
  125. {subliminal-2.3.2 → subliminal-2.5.0}/scripts/.gitignore +0 -0
  126. {subliminal-2.3.2 → subliminal-2.5.0}/scripts/prepare-release-pr.py +0 -0
  127. {subliminal-2.3.2 → subliminal-2.5.0}/scripts/prepare_tests.py +0 -0
  128. {subliminal-2.3.2 → subliminal-2.5.0}/scripts/release.py +0 -0
  129. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/__init__.py +0 -0
  130. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/__main__.py +0 -0
  131. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/archives.py +0 -0
  132. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/cache.py +0 -0
  133. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/converters/__init__.py +0 -0
  134. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/converters/addic7ed.py +0 -0
  135. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/converters/opensubtitles.py +0 -0
  136. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/converters/opensubtitlescom.py +0 -0
  137. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/converters/subtitulamos.py +0 -0
  138. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/converters/tvsubtitles.py +0 -0
  139. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/exceptions.py +0 -0
  140. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/matches.py +0 -0
  141. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/__init__.py +0 -0
  142. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/addic7ed.py +0 -0
  143. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/bsplayer.py +0 -0
  144. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/gestdown.py +0 -0
  145. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/mock.py +0 -0
  146. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/napiprojekt.py +0 -0
  147. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/podnapisi.py +0 -0
  148. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/providers/tvsubtitles.py +0 -0
  149. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/py.typed +0 -0
  150. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/refiners/__init__.py +0 -0
  151. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/refiners/hash.py +0 -0
  152. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/refiners/metadata.py +0 -0
  153. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/refiners/tmdb.py +0 -0
  154. {subliminal-2.3.2 → subliminal-2.5.0}/src/subliminal/subtitle.py +0 -0
  155. {subliminal-2.3.2 → subliminal-2.5.0}/tests/__init__.py +0 -0
  156. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_download_subtitle.yaml +0 -0
  157. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_id.yaml +0 -0
  158. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_id_country.yaml +0 -0
  159. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_id_quote_dots_mixed_case.yaml +0 -0
  160. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_id_with_comma.yaml +0 -0
  161. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_id_year.yaml +0 -0
  162. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_get_show_ids.yaml +0 -0
  163. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_list_subtitles.yaml +0 -0
  164. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_list_subtitles_episode_alternative_series.yaml +0 -0
  165. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_list_subtitles_show_with_asterisk.yaml +0 -0
  166. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query.yaml +0 -0
  167. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_no_year.yaml +0 -0
  168. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_parsing.yaml +0 -0
  169. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_parsing_colon.yaml +0 -0
  170. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_parsing_dash.yaml +0 -0
  171. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_parsing_quote_dots_mixed_case.yaml +0 -0
  172. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_wrong_series.yaml +0 -0
  173. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/addic7ed/test_query_year.yaml +0 -0
  174. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/bsplayer/test_download_subtitle.yaml +0 -0
  175. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/bsplayer/test_list_subtitles_hash.yaml +0 -0
  176. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/bsplayer/test_login.yaml +0 -0
  177. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/bsplayer/test_logout.yaml +0 -0
  178. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/bsplayer/test_query_hash_size.yaml +0 -0
  179. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/cli/test_cli_download.yaml +0 -0
  180. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_download_subtitle.yaml +0 -0
  181. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_download_with_bom.yaml +0 -0
  182. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_get_title_and_show_id_alternative_name.yaml +0 -0
  183. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_get_title_and_show_id_no_year.yaml +0 -0
  184. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_get_title_and_show_id_only_title.yaml +0 -0
  185. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_get_title_and_show_id_with_tvdb_id.yaml +0 -0
  186. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_list_subtitles.yaml +0 -0
  187. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_list_subtitles_episode_alternative_series.yaml +0 -0
  188. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query.yaml +0 -0
  189. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_all_series.yaml +0 -0
  190. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_no_language.yaml +0 -0
  191. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_parsing.yaml +0 -0
  192. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_parsing_colon.yaml +0 -0
  193. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_parsing_dash.yaml +0 -0
  194. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_parsing_quote_dots_mixed_case.yaml +0 -0
  195. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_query_wrong_series.yaml +0 -0
  196. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_id.yaml +0 -0
  197. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_id_error.yaml +0 -0
  198. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_id_incomplete.yaml +0 -0
  199. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_id_no_year.yaml +0 -0
  200. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_id_quote.yaml +0 -0
  201. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_search_show_series_tvdb_id.yaml +0 -0
  202. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/gestdown/test_show_with_asterisk.yaml +0 -0
  203. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_download_subtitles.yaml +0 -0
  204. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_list_subtitles.yaml +0 -0
  205. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_query_microdvd.yaml +0 -0
  206. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_query_srt.yaml +0 -0
  207. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_query_srt_reencode.yaml +0 -0
  208. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/napiprojekt/test_query_wrong_hash.yaml +0 -0
  209. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_get_id.yaml +0 -0
  210. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_get_title.yaml +0 -0
  211. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_get_wrong_id.yaml +0 -0
  212. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_get_wrong_title.yaml +0 -0
  213. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_episode.yaml +0 -0
  214. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_episode_original_series.yaml +0 -0
  215. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_episode_with_country.yaml +0 -0
  216. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
  217. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_episode_year.yaml +0 -0
  218. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_movie.yaml +0 -0
  219. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_refine_movie_guess_alternative_title.yaml +0 -0
  220. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_search.yaml +0 -0
  221. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_search_page.yaml +0 -0
  222. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_search_type.yaml +0 -0
  223. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_search_wrong_title.yaml +0 -0
  224. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/omdb/test_search_year.yaml +0 -0
  225. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/opensubtitlescom/test_query_wrong_hash_wrong_size.yaml +0 -0
  226. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_download_subtitle.yaml +0 -0
  227. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_download_subtitles_with_title_unicode.yaml +0 -0
  228. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_list_subtitles_episode.yaml +0 -0
  229. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_list_subtitles_episode_alternative_series.yaml +0 -0
  230. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_list_subtitles_movie.yaml +0 -0
  231. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_query_episode.yaml +0 -0
  232. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/podnapisi/test_query_movie.yaml +0 -0
  233. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/providers/test_download_best_subtitles.yaml +0 -0
  234. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/providers/test_download_best_subtitles_min_score.yaml +0 -0
  235. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/providers/test_download_best_subtitles_only_one.yaml +0 -0
  236. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_download_subtitle.yaml +0 -0
  237. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_download_subtitle_first_episode.yaml +0 -0
  238. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_download_subtitle_foo.yaml +0 -0
  239. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_download_subtitle_last_season.yaml +0 -0
  240. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_download_subtitle_year.yaml +0 -0
  241. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_episode.yaml +0 -0
  242. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_language.yaml +0 -0
  243. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_season.yaml +0 -0
  244. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_list_subtitles_not_exist_series.yaml +0 -0
  245. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/subtitulamos/test_list_subtitles_with_spanish_non_mx_search.yaml +0 -0
  246. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_get_movie_id.yaml +0 -0
  247. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_get_movie_id_failed.yaml +0 -0
  248. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_get_series_id.yaml +0 -0
  249. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_get_series_wrong_id.yaml +0 -0
  250. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_episode.yaml +0 -0
  251. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_episode_original_series.yaml +0 -0
  252. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_episode_with_country.yaml +0 -0
  253. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
  254. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_episode_year.yaml +0 -0
  255. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_movie.yaml +0 -0
  256. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_refine_movie_guess_alternative_title.yaml +0 -0
  257. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_search_movie.yaml +0 -0
  258. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_search_movie_wrong_title.yaml +0 -0
  259. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_search_movie_year.yaml +0 -0
  260. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_search_series.yaml +0 -0
  261. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tmdb/test_search_series_wrong_name.yaml +0 -0
  262. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_get_series.yaml +0 -0
  263. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_get_series_actors.yaml +0 -0
  264. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_get_series_actors_wrong_id.yaml +0 -0
  265. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_get_series_wrong_id.yaml +0 -0
  266. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_login.yaml +0 -0
  267. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_login_error.yaml +0 -0
  268. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_query_series_episodes.yaml +0 -0
  269. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_query_series_episodes_wrong_season.yaml +0 -0
  270. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine.yaml +0 -0
  271. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_ambiguous.yaml +0 -0
  272. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_ambiguous_2.yaml +0 -0
  273. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_alternative_series.yaml +0 -0
  274. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_no_year.yaml +0 -0
  275. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_partial.yaml +0 -0
  276. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_with_comma.yaml +0 -0
  277. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_with_country.yaml +0 -0
  278. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_with_country_hoc_us.yaml +0 -0
  279. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refine_episode_year.yaml +0 -0
  280. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_refresh_token.yaml +0 -0
  281. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_search_series.yaml +0 -0
  282. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_search_series_wrong_name.yaml +0 -0
  283. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvdb/test_token_needs_refresh.yaml +0 -0
  284. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_download_subtitle.yaml +0 -0
  285. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_download_subtitle_with_quote.yaml +0 -0
  286. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_get_episode_ids.yaml +0 -0
  287. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_get_episode_ids_wrong_season.yaml +0 -0
  288. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_list_subtitles.yaml +0 -0
  289. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_list_subtitles_episode_alternative_series.yaml +0 -0
  290. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_query.yaml +0 -0
  291. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_query_no_year.yaml +0 -0
  292. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_query_with_quote.yaml +0 -0
  293. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_query_wrong_episode.yaml +0 -0
  294. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_query_wrong_series.yaml +0 -0
  295. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id.yaml +0 -0
  296. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_ambiguous.yaml +0 -0
  297. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_error.yaml +0 -0
  298. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_incomplete.yaml +0 -0
  299. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_no_year.yaml +0 -0
  300. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_uk.yaml +0 -0
  301. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_us.yaml +0 -0
  302. {subliminal-2.3.2 → subliminal-2.5.0}/tests/cassettes/tvsubtitles/test_search_show_id_year_in_title.yaml +0 -0
  303. {subliminal-2.3.2 → subliminal-2.5.0}/tests/providers/test_addic7ed.py +0 -0
  304. {subliminal-2.3.2 → subliminal-2.5.0}/tests/providers/test_bsplayer.py +0 -0
  305. {subliminal-2.3.2 → subliminal-2.5.0}/tests/providers/test_gestdown.py +0 -0
  306. {subliminal-2.3.2 → subliminal-2.5.0}/tests/providers/test_napiprojekt.py +0 -0
  307. {subliminal-2.3.2 → subliminal-2.5.0}/tests/providers/test_opensubtitles.py +0 -0
  308. {subliminal-2.3.2 → subliminal-2.5.0}/tests/providers/test_podnapisi.py +0 -0
  309. {subliminal-2.3.2 → subliminal-2.5.0}/tests/providers/test_subtitulamos.py +0 -0
  310. {subliminal-2.3.2 → subliminal-2.5.0}/tests/providers/test_tvsubtitles.py +0 -0
  311. {subliminal-2.3.2 → subliminal-2.5.0}/tests/refiners/test_hash.py +0 -0
  312. {subliminal-2.3.2 → subliminal-2.5.0}/tests/refiners/test_metadata.py +0 -0
  313. {subliminal-2.3.2 → subliminal-2.5.0}/tests/refiners/test_omdb.py +0 -0
  314. {subliminal-2.3.2 → subliminal-2.5.0}/tests/refiners/test_tvdb.py +0 -0
  315. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_cache.py +0 -0
  316. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_extensions.py +0 -0
  317. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_matches.py +0 -0
  318. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_provider.py +0 -0
  319. {subliminal-2.3.2 → subliminal-2.5.0}/tests/test_score.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
- body_path: latest-release-notes.md
81
+ generate_release_notes: true
110
82
  draft: true
@@ -0,0 +1,13 @@
1
+ rules:
2
+ dangerous-triggers:
3
+ ignore:
4
+ - coverage.yaml:3:1
5
+ unpinned-uses:
6
+ ignore:
7
+ - action.yaml
8
+ - CI.yaml
9
+ - coverage.yaml
10
+ - docker-build.yaml
11
+ - publish-release.yaml
12
+ - publish.yaml
13
+ - tag-release.yaml
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/pre-commit/pre-commit-hooks
3
- rev: v5.0.0
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.30.3
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.11.2
16
+ rev: v0.14.9
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: v1.1.2
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.5.2
36
+ rev: v1.18.0
37
37
  hooks:
38
38
  - id: zizmor
39
39
 
@@ -2,6 +2,60 @@
2
2
 
3
3
  .. towncrier release notes start
4
4
 
5
+ 2.5.0 (2025-12-14)
6
+ ^^^^^^^^^^^^^^^^^^
7
+
8
+ Provider Changes
9
+ ----------------
10
+
11
+ - OpenSubtitlesCom: add a max_result_pages parameter to limit the number of result pages to speed up the search (`#1321 <https://github.com/Diaoul/subliminal/issues/1321>`__)
12
+
13
+
14
+ Documentation
15
+ -------------
16
+
17
+ - fix ``generate_default_config`` for ``click==8.3`` and pin to ``click<8.3`` until another regression is fixed. (`#1318 <https://github.com/Diaoul/subliminal/issues/1318>`__)
18
+
19
+
20
+ Misc
21
+ ----
22
+
23
+ - add support for python 3.14 (`#1317 <https://github.com/Diaoul/subliminal/issues/1317>`__)
24
+ - Fix test with click 8.3.0 running in parallel (`#1320 <https://github.com/Diaoul/subliminal/issues/1320>`__)
25
+ - fix test on big-endian system (`#1323 <https://github.com/Diaoul/subliminal/issues/1323>`__)
26
+ - compatibility with stevedore 5.6.0 (`#1325 <https://github.com/Diaoul/subliminal/issues/1325>`__)
27
+ - fix some tests to pass in a dirty environment (`#1326 <https://github.com/Diaoul/subliminal/issues/1326>`__)
28
+ - update pre-commit hook versions (`#1329 <https://github.com/Diaoul/subliminal/issues/1329>`__)
29
+ - Fix some test so they are more reliable and stable (`#1331 <https://github.com/Diaoul/subliminal/issues/1331>`__)
30
+
31
+
32
+ 2.4.0 (2025-09-03)
33
+ ^^^^^^^^^^^^^^^^^^
34
+
35
+ Changes
36
+ -------
37
+
38
+ - score: add fps match, remove hearing_impaired match (`#1250 <https://github.com/Diaoul/subliminal/issues/1250>`__)
39
+
40
+
41
+ CLI Changes
42
+ -----------
43
+
44
+ - cli: refactor the cli.py file into a folder (`#1299 <https://github.com/Diaoul/subliminal/issues/1299>`__)
45
+
46
+
47
+ Documentation
48
+ -------------
49
+
50
+ - doc: add a section in the README to run subliminal with the ``--debug`` flag. (`#1308 <https://github.com/Diaoul/subliminal/issues/1308>`__)
51
+
52
+
53
+ Misc
54
+ ----
55
+
56
+ - ensure Episode.series is always a string, not a list from guessit bug (`#1304 <https://github.com/Diaoul/subliminal/issues/1304>`__)
57
+
58
+
5
59
  2.3.2 (2025-05-08)
6
60
  ^^^^^^^^^^^^^^^^^^
7
61
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: subliminal
3
- Version: 2.3.2
3
+ Version: 2.5.0
4
4
  Summary: Subtitles, faster than your thoughts
5
5
  Project-URL: homepage, https://github.com/Diaoul/subliminal
6
6
  Project-URL: repository, https://github.com/Diaoul/subliminal
@@ -21,14 +21,15 @@ Classifier: Programming Language :: Python :: 3.10
21
21
  Classifier: Programming Language :: Python :: 3.11
22
22
  Classifier: Programming Language :: Python :: 3.12
23
23
  Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
24
25
  Classifier: Topic :: Multimedia :: Video
25
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
27
  Requires-Python: >=3.9
27
28
  Requires-Dist: babelfish>=0.6.1
28
29
  Requires-Dist: beautifulsoup4>=4.4.0
29
30
  Requires-Dist: chardet>=5.0
31
+ Requires-Dist: click
30
32
  Requires-Dist: click-option-group>=0.5.6
31
- Requires-Dist: click>=8.0
32
33
  Requires-Dist: defusedxml>=0.7.1
33
34
  Requires-Dist: dogpile-cache>=1.0
34
35
  Requires-Dist: guessit>=3.0.0
@@ -56,7 +57,6 @@ Provides-Extra: tests
56
57
  Requires-Dist: colorama; extra == 'tests'
57
58
  Requires-Dist: coverage[toml]>=7; extra == 'tests'
58
59
  Requires-Dist: importlib-metadata>=4.6; (python_version < '3.10') and extra == 'tests'
59
- Requires-Dist: pypandoc; extra == 'tests'
60
60
  Requires-Dist: pytest-cov; extra == 'tests'
61
61
  Requires-Dist: pytest-xdist; extra == 'tests'
62
62
  Requires-Dist: pytest>=6.0; extra == 'tests'
@@ -159,6 +159,14 @@ Run subliminal in a docker container::
159
159
 
160
160
  $ 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
161
 
162
+ Debugging
163
+ ^^^^^^^^^
164
+
165
+ By default, subliminal output is minimal. Run with the `--debug` flag before the `download` command to get more information::
166
+
167
+ $ subliminal --debug download -l en The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.mp4
168
+
169
+
162
170
  Installation
163
171
  ------------
164
172
  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://docs.python-requests.org/en/latest/', None),
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'),
@@ -24,6 +24,10 @@ CLI arguments (like ``path``) cannot be specified in the configuration file.
24
24
 
25
25
  Options that can be used multiple times (like ``--language``) need to be defined as arrays.
26
26
 
27
+ ``hearing_impaired`` and ``foreign_only`` can take three values: "true", "false" or "[]".
28
+ They mean that hearing impaired (or foreign-only) subtitles will be, respectively,
29
+ ranked first, ranked last or not given any particular rank, for subtitles with identical scores.
30
+
27
31
 
28
32
  Example
29
33
  -------
@@ -40,5 +44,5 @@ Default configuration
40
44
  Here is a list of all the options of the configuration file.
41
45
  Commented options need a value to be valid.
42
46
 
43
- .. program-output:: python -c "from subliminal.cli import generate_default_config; print(generate_default_config(commented=False))"
47
+ .. program-output:: python -c "from subliminal.cli.generate_config import generate_default_config; print(generate_default_config(commented=False))"
44
48
  :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]>: 789}
150
- {<PodnapisiSubtitle 'ONAW' [hu]>: 772}
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
 
@@ -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>=8.0",
42
+ "click",
42
43
  "click-option-group>=0.5.6",
43
44
  "dogpile.cache>=1.0",
44
45
  "guessit>=3.0.0",
@@ -72,7 +73,6 @@ tests = [
72
73
  "pytest-xdist",
73
74
  "sympy",
74
75
  "colorama", # used in scripts/
75
- "pypandoc", # used in scripts/
76
76
  "vcrpy>=5", # keep synchronized with docs dependencies
77
77
  "win32-setctime; sys_platform=='win32'",
78
78
  "importlib_metadata>=4.6; python_version<'3.10'",
@@ -275,12 +275,12 @@ showcontent = true
275
275
  [[tool.towncrier.type]]
276
276
  directory = "doc"
277
277
  name = "Documentation"
278
- showcontent = false
278
+ showcontent = true
279
279
 
280
280
  [[tool.towncrier.type]]
281
281
  directory = "misc"
282
282
  name = "Misc"
283
- showcontent = false
283
+ showcontent = true
284
284
 
285
285
 
286
286
  # https://mypy.readthedocs.io/en/stable/config_file.html
@@ -0,0 +1,6 @@
1
+ """CLI module."""
2
+
3
+ from .cli import cli
4
+ from .generate_config import generate_default_config
5
+
6
+ __all__ = ['cli', 'generate_default_config']
@@ -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,5 @@
1
+ """CLI subcommands."""
2
+
3
+ from .download_best import download
4
+
5
+ __all__ = ['download']
@@ -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
+ )