ytdl-sub 2026.1.24.post1__tar.gz → 2026.1.27__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 (172) hide show
  1. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/PKG-INFO +1 -1
  2. ytdl_sub-2026.1.27/src/ytdl_sub/__init__.py +1 -0
  3. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/overrides.py +16 -37
  4. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/plugin/plugin.py +1 -1
  5. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/preset_options.py +1 -1
  6. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/downloaders/url/downloader.py +6 -8
  7. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/downloaders/url/validators.py +2 -1
  8. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/date_range.py +1 -1
  9. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/embed_thumbnail.py +1 -1
  10. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/filter_exclude.py +8 -3
  11. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/filter_include.py +7 -5
  12. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/nfo_tags.py +1 -1
  13. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/square_thumbnail.py +1 -1
  14. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/throttle_protection.py +3 -3
  15. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/subscriptions/subscription_download.py +2 -2
  16. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/subscriptions/subscription_ytdl_options.py +2 -2
  17. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/string_formatter_validators.py +38 -32
  18. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub.egg-info/PKG-INFO +1 -1
  19. ytdl_sub-2026.1.24.post1/src/ytdl_sub/__init__.py +0 -1
  20. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/LICENSE +0 -0
  21. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/README.md +0 -0
  22. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/pyproject.toml +0 -0
  23. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/setup.cfg +0 -0
  24. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/cli/__init__.py +0 -0
  25. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/cli/entrypoint.py +0 -0
  26. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/cli/output_summary.py +0 -0
  27. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/cli/output_transaction_log.py +0 -0
  28. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/cli/parsers/__init__.py +0 -0
  29. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/cli/parsers/cli_to_sub.py +0 -0
  30. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/cli/parsers/dl.py +0 -0
  31. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/cli/parsers/main.py +0 -0
  32. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/__init__.py +0 -0
  33. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/config_file.py +0 -0
  34. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/config_validator.py +0 -0
  35. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/defaults.py +0 -0
  36. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/plugin/__init__.py +0 -0
  37. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/plugin/plugin_mapping.py +0 -0
  38. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/plugin/plugin_operation.py +0 -0
  39. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/plugin/preset_plugins.py +0 -0
  40. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/preset.py +0 -0
  41. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/validators/__init__.py +0 -0
  42. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/validators/options.py +0 -0
  43. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/config/validators/variable_validation.py +0 -0
  44. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/downloaders/__init__.py +0 -0
  45. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/downloaders/info_json/__init__.py +0 -0
  46. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/downloaders/info_json/info_json_downloader.py +0 -0
  47. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/downloaders/source_plugin.py +0 -0
  48. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/downloaders/url/__init__.py +0 -0
  49. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/downloaders/ytdl_options_builder.py +0 -0
  50. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/downloaders/ytdlp.py +0 -0
  51. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/__init__.py +0 -0
  52. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/base_entry.py +0 -0
  53. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/entry.py +0 -0
  54. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/entry_parent.py +0 -0
  55. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/script/__init__.py +0 -0
  56. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/script/custom_functions.py +0 -0
  57. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/script/function_scripts.py +0 -0
  58. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/script/variable_definitions.py +0 -0
  59. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/script/variable_types.py +0 -0
  60. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/variables/__init__.py +0 -0
  61. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/entries/variables/override_variables.py +0 -0
  62. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/main.py +0 -0
  63. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/__init__.py +0 -0
  64. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/audio_extract.py +0 -0
  65. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/chapters.py +0 -0
  66. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/file_convert.py +0 -0
  67. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/format.py +0 -0
  68. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/internal/__init__.py +0 -0
  69. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/internal/view.py +0 -0
  70. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/match_filters.py +0 -0
  71. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/music_tags.py +0 -0
  72. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/output_directory_nfo_tags.py +0 -0
  73. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/split_by_chapters.py +0 -0
  74. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/static_nfo_tags.py +0 -0
  75. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/subtitles.py +0 -0
  76. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/plugins/video_tags.py +0 -0
  77. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/__init__.py +0 -0
  78. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/helpers/__init__.py +0 -0
  79. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/helpers/download_deletion_options.yaml +0 -0
  80. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/helpers/filter_duration.yaml +0 -0
  81. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/helpers/filter_keywords.yaml +0 -0
  82. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/helpers/media_quality.yaml +0 -0
  83. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/helpers/players.yaml +0 -0
  84. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/helpers/throttle_protection.yaml +0 -0
  85. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/helpers/url.yaml +0 -0
  86. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/helpers/url_bilateral.yaml +0 -0
  87. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/helpers/url_categorized.yaml +0 -0
  88. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/internal/__init__.py +0 -0
  89. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/internal/view.yaml +0 -0
  90. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/music/__init__.py +0 -0
  91. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/music/albums_from_chapters.yaml +0 -0
  92. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/music/albums_from_playlists.yaml +0 -0
  93. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/music/singles.yaml +0 -0
  94. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/music/soundcloud.yaml +0 -0
  95. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/music_videos/__init__.py +0 -0
  96. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_base.yaml +0 -0
  97. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_extras.yaml +0 -0
  98. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/music_videos/music_videos.yaml +0 -0
  99. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/tv_show/__init__.py +0 -0
  100. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/tv_show/episode.yaml +0 -0
  101. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show.yaml +0 -0
  102. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_by_date.yaml +0 -0
  103. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_collection.yaml +0 -0
  104. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/__init__.py +0 -0
  105. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/__init__.py +0 -0
  106. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/array_functions.py +0 -0
  107. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/boolean_functions.py +0 -0
  108. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/conditional_functions.py +0 -0
  109. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/date_functions.py +0 -0
  110. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/error_functions.py +0 -0
  111. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/json_functions.py +0 -0
  112. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/map_functions.py +0 -0
  113. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/numeric_functions.py +0 -0
  114. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/print_functions.py +0 -0
  115. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/regex_functions.py +0 -0
  116. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/functions/string_functions.py +0 -0
  117. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/parser.py +0 -0
  118. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/script.py +0 -0
  119. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/script_output.py +0 -0
  120. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/types/__init__.py +0 -0
  121. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/types/array.py +0 -0
  122. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/types/function.py +0 -0
  123. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/types/map.py +0 -0
  124. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/types/resolvable.py +0 -0
  125. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/types/syntax_tree.py +0 -0
  126. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/types/variable.py +0 -0
  127. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/types/variable_dependency.py +0 -0
  128. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/utils/__init__.py +0 -0
  129. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/utils/exception_formatters.py +0 -0
  130. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/utils/exceptions.py +0 -0
  131. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/utils/name_validation.py +0 -0
  132. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/script/utils/type_checking.py +0 -0
  133. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/subscriptions/__init__.py +0 -0
  134. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/subscriptions/base_subscription.py +0 -0
  135. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/subscriptions/subscription.py +0 -0
  136. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/subscriptions/subscription_validators.py +0 -0
  137. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/thread/__init__.py +0 -0
  138. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/thread/log_entries_downloaded_listener.py +0 -0
  139. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/__init__.py +0 -0
  140. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/chapters.py +0 -0
  141. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/datetime.py +0 -0
  142. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/exceptions.py +0 -0
  143. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/ffmpeg.py +0 -0
  144. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/file_handler.py +0 -0
  145. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/file_lock.py +0 -0
  146. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/file_path.py +0 -0
  147. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/logger.py +0 -0
  148. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/retry.py +0 -0
  149. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/script.py +0 -0
  150. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/scriptable.py +0 -0
  151. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/subtitles.py +0 -0
  152. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/system.py +0 -0
  153. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/thumbnail.py +0 -0
  154. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/xml.py +0 -0
  155. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/utils/yaml.py +0 -0
  156. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/__init__.py +0 -0
  157. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/audo_codec_validator.py +0 -0
  158. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/file_path_validators.py +0 -0
  159. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/nfo_validators.py +0 -0
  160. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/regex_validator.py +0 -0
  161. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/source_variable_validator.py +0 -0
  162. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/strict_dict_validator.py +0 -0
  163. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/string_datetime.py +0 -0
  164. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/string_select_validator.py +0 -0
  165. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/validators/validators.py +0 -0
  166. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/ytdl_additions/__init__.py +0 -0
  167. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub/ytdl_additions/enhanced_download_archive.py +0 -0
  168. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub.egg-info/SOURCES.txt +0 -0
  169. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub.egg-info/dependency_links.txt +0 -0
  170. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub.egg-info/entry_points.txt +0 -0
  171. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub.egg-info/requires.txt +0 -0
  172. {ytdl_sub-2026.1.24.post1 → ytdl_sub-2026.1.27}/src/ytdl_sub.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ytdl-sub
3
- Version: 2026.1.24.post1
3
+ Version: 2026.1.27
4
4
  Summary: Automate downloading metadata generation with YoutubeDL
5
5
  Author: Jesse Bannon
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -0,0 +1 @@
1
+ __pypi_version__ = "2026.01.27";__local_version__ = "2026.01.27+d37945d"
@@ -3,6 +3,8 @@ from typing import Dict
3
3
  from typing import Iterable
4
4
  from typing import Optional
5
5
  from typing import Set
6
+ from typing import Type
7
+ from typing import TypeVar
6
8
 
7
9
  from ytdl_sub.entries.entry import Entry
8
10
  from ytdl_sub.entries.script.variable_definitions import VARIABLES
@@ -20,10 +22,11 @@ from ytdl_sub.utils.exceptions import StringFormattingException
20
22
  from ytdl_sub.utils.exceptions import ValidationException
21
23
  from ytdl_sub.utils.script import ScriptUtils
22
24
  from ytdl_sub.utils.scriptable import Scriptable
23
- from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
24
25
  from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
25
26
  from ytdl_sub.validators.string_formatter_validators import UnstructuredDictFormatterValidator
26
27
 
28
+ ExpectedT = TypeVar("ExpectedT")
29
+
27
30
 
28
31
  class Overrides(UnstructuredDictFormatterValidator, Scriptable):
29
32
  """
@@ -207,7 +210,8 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable):
207
210
  formatter: StringFormatterValidator,
208
211
  entry: Optional[Entry] = None,
209
212
  function_overrides: Optional[Dict[str, str]] = None,
210
- ) -> str:
213
+ expected_type: Type[ExpectedT] = str,
214
+ ) -> ExpectedT:
211
215
  """
212
216
  Parameters
213
217
  ----------
@@ -217,6 +221,8 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable):
217
221
  Optional. Entry to add source variables to the formatter
218
222
  function_overrides
219
223
  Optional. Explicit values to override the overrides themselves and source variables
224
+ expected_type
225
+ The expected type that should return. Defaults to string.
220
226
 
221
227
  Returns
222
228
  -------
@@ -227,42 +233,15 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable):
227
233
  StringFormattingException
228
234
  If the formatter that is trying to be resolved cannot
229
235
  """
230
- return formatter.post_process(
231
- str(
232
- self._apply_to_resolvable(
233
- formatter=formatter, entry=entry, function_overrides=function_overrides
234
- )
235
- )
236
- )
237
-
238
- def apply_overrides_formatter_to_native(
239
- self,
240
- formatter: OverridesStringFormatterValidator,
241
- function_overrides: Optional[Dict[str, str]] = None,
242
- ) -> Any:
243
- """
244
- Parameters
245
- ----------
246
- formatter
247
- Overrides formatter to apply
248
- function_overrides
249
- Optional. Explicit values to override the overrides themselves and source variables
250
-
251
- Returns
252
- -------
253
- The native python form of the resolved variable
254
- """
255
- return formatter.post_process_native(
236
+ out = formatter.post_process(
256
237
  self._apply_to_resolvable(
257
- formatter=formatter, entry=None, function_overrides=function_overrides
238
+ formatter=formatter, entry=entry, function_overrides=function_overrides
258
239
  ).native
259
240
  )
260
241
 
261
- def evaluate_boolean(
262
- self, formatter: StringFormatterValidator, entry: Optional[Entry] = None
263
- ) -> bool:
264
- """
265
- Apply a formatter, and evaluate it to a boolean
266
- """
267
- output = self.apply_formatter(formatter=formatter, entry=entry)
268
- return ScriptUtils.bool_formatter_output(output)
242
+ if not isinstance(out, expected_type):
243
+ raise StringFormattingException(
244
+ f"Expected type {expected_type.__name__}, but received '{out.__class__.__name__}'"
245
+ )
246
+
247
+ return out
@@ -48,7 +48,7 @@ class Plugin(BasePlugin[OptionsValidatorT], Generic[OptionsValidatorT], ABC):
48
48
  Returns True if enabled, False if disabled.
49
49
  """
50
50
  if isinstance(self.plugin_options, ToggleableOptionsDictValidator):
51
- return self.overrides.evaluate_boolean(self.plugin_options.enable)
51
+ return self.overrides.apply_formatter(self.plugin_options.enable, expected_type=bool)
52
52
  return True
53
53
 
54
54
  def ytdl_options_match_filters(self) -> Tuple[List[str], List[str]]:
@@ -63,7 +63,7 @@ class YTDLOptions(UnstructuredOverridesDictFormatterValidator):
63
63
  native python.
64
64
  """
65
65
  out = {
66
- key: overrides.apply_overrides_formatter_to_native(val)
66
+ key: overrides.apply_formatter(val, expected_type=object)
67
67
  for key, val in self.dict.items()
68
68
  }
69
69
  if "cookiefile" in out:
@@ -52,12 +52,12 @@ class UrlDownloaderBasePluginExtension(SourcePluginExtension[MultiUrlValidator])
52
52
 
53
53
  if 0 <= input_url_idx < len(self.plugin_options.urls.list):
54
54
  validator = self.plugin_options.urls.list[input_url_idx]
55
- if entry_input_url in self.overrides.apply_overrides_formatter_to_native(validator.url):
55
+ if entry_input_url in self.overrides.apply_formatter(validator.url, expected_type=list):
56
56
  return validator
57
57
 
58
58
  # Match the first validator based on the URL, if one exists
59
59
  for validator in self.plugin_options.urls.list:
60
- if entry_input_url in self.overrides.apply_overrides_formatter_to_native(validator.url):
60
+ if entry_input_url in self.overrides.apply_formatter(validator.url, expected_type=list):
61
61
  return validator
62
62
 
63
63
  # Return the first validator if none exist
@@ -382,7 +382,7 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
382
382
  entries_to_iter: List[Optional[Entry]] = entries
383
383
 
384
384
  indices = list(range(len(entries_to_iter)))
385
- if self.overrides.evaluate_boolean(validator.download_reverse):
385
+ if self.overrides.apply_formatter(validator.download_reverse, expected_type=bool):
386
386
  indices = reversed(indices)
387
387
 
388
388
  for idx in indices:
@@ -461,8 +461,8 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
461
461
  ytdl_option_overrides=validator.ytdl_options.to_native_dict(self.overrides)
462
462
  )
463
463
 
464
- include_sibling_metadata = self.overrides.evaluate_boolean(
465
- validator.include_sibling_metadata
464
+ include_sibling_metadata = self.overrides.apply_formatter(
465
+ validator.include_sibling_metadata, expected_type=bool
466
466
  )
467
467
 
468
468
  parents, orphan_entries = self._download_url_metadata(
@@ -487,11 +487,9 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
487
487
  # download the bottom-most urls first since they are top-priority
488
488
  for idx, url_validator in reversed(list(enumerate(self.collection.urls.list))):
489
489
  # URLs can be empty. If they are, then skip
490
- if not (urls := self.overrides.apply_overrides_formatter_to_native(url_validator.url)):
490
+ if not (urls := self.overrides.apply_formatter(url_validator.url, expected_type=list)):
491
491
  continue
492
492
 
493
- assert isinstance(urls, list)
494
-
495
493
  for url in reversed(urls):
496
494
  assert isinstance(url, str)
497
495
 
@@ -1,5 +1,6 @@
1
1
  from typing import Any
2
2
  from typing import Dict
3
+ from typing import List
3
4
  from typing import Optional
4
5
  from typing import Set
5
6
 
@@ -44,7 +45,7 @@ class UrlThumbnailListValidator(ListValidator[UrlThumbnailValidator]):
44
45
 
45
46
 
46
47
  class OverridesOneOrManyUrlValidator(OverridesStringFormatterValidator):
47
- def post_process_native(self, resolved: Any) -> Any:
48
+ def post_process(self, resolved: Any) -> List[str]:
48
49
  if isinstance(resolved, str):
49
50
  return [resolved]
50
51
  if isinstance(resolved, list):
@@ -116,7 +116,7 @@ class DateRangePlugin(Plugin[DateRangeOptions]):
116
116
  date_validator=self.plugin_options.after, overrides=self.overrides
117
117
  )
118
118
  after_filter = f"{date_type} >= {after_str}"
119
- if self.overrides.evaluate_boolean(self.plugin_options.breaks):
119
+ if self.overrides.apply_formatter(self.plugin_options.breaks, expected_type=bool):
120
120
  breaking_match_filters.append(after_filter)
121
121
  else:
122
122
  match_filters.append(after_filter)
@@ -33,7 +33,7 @@ class EmbedThumbnailPlugin(Plugin[EmbedThumbnailOptions]):
33
33
 
34
34
  @property
35
35
  def _embed_thumbnail(self) -> bool:
36
- return self.overrides.evaluate_boolean(self.plugin_options)
36
+ return self.overrides.apply_formatter(self.plugin_options, expected_type=bool)
37
37
 
38
38
  @classmethod
39
39
  def _embed_video_thumbnail(cls, entry: Entry) -> None:
@@ -7,13 +7,14 @@ from ytdl_sub.config.validators.options import OptionsValidator
7
7
  from ytdl_sub.entries.entry import Entry
8
8
  from ytdl_sub.utils.exceptions import StringFormattingException
9
9
  from ytdl_sub.utils.logger import Logger
10
- from ytdl_sub.validators.string_formatter_validators import ListFormatterValidator
10
+ from ytdl_sub.validators.string_formatter_validators import BooleanFormatterValidator
11
+ from ytdl_sub.validators.validators import ListValidator
11
12
  from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
12
13
 
13
14
  logger = Logger.get("filter-exclude")
14
15
 
15
16
 
16
- class FilterExcludeOptions(ListFormatterValidator, OptionsValidator):
17
+ class FilterExcludeOptions(ListValidator[BooleanFormatterValidator], OptionsValidator):
17
18
  """
18
19
  Applies a conditional OR on any number of filters comprised of either variables or scripts.
19
20
  If any filter evaluates to True, the entry will be excluded.
@@ -29,6 +30,8 @@ class FilterExcludeOptions(ListFormatterValidator, OptionsValidator):
29
30
  { %contains( %lower(description), '#short' ) }
30
31
  """
31
32
 
33
+ _inner_list_type = BooleanFormatterValidator
34
+
32
35
 
33
36
  class FilterExcludePlugin(Plugin[FilterExcludeOptions]):
34
37
  plugin_options_type = FilterExcludeOptions
@@ -52,7 +55,9 @@ class FilterExcludePlugin(Plugin[FilterExcludeOptions]):
52
55
  return entry
53
56
 
54
57
  for formatter in self.plugin_options.list:
55
- should_exclude = self.overrides.evaluate_boolean(formatter=formatter, entry=entry)
58
+ should_exclude = self.overrides.apply_formatter(
59
+ formatter=formatter, entry=entry, expected_type=bool
60
+ )
56
61
 
57
62
  if should_exclude:
58
63
  logger.info(
@@ -7,14 +7,14 @@ from ytdl_sub.config.validators.options import OptionsValidator
7
7
  from ytdl_sub.entries.entry import Entry
8
8
  from ytdl_sub.utils.exceptions import StringFormattingException
9
9
  from ytdl_sub.utils.logger import Logger
10
- from ytdl_sub.utils.script import ScriptUtils
11
- from ytdl_sub.validators.string_formatter_validators import ListFormatterValidator
10
+ from ytdl_sub.validators.string_formatter_validators import BooleanFormatterValidator
11
+ from ytdl_sub.validators.validators import ListValidator
12
12
  from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
13
13
 
14
14
  logger = Logger.get("filter-include")
15
15
 
16
16
 
17
- class FilterIncludeOptions(ListFormatterValidator, OptionsValidator):
17
+ class FilterIncludeOptions(ListValidator[BooleanFormatterValidator], OptionsValidator):
18
18
  """
19
19
  Applies a conditional AND on any number of filters comprised of either variables or scripts.
20
20
  If all filters evaluate to True, the entry will be included.
@@ -38,6 +38,8 @@ class FilterIncludeOptions(ListFormatterValidator, OptionsValidator):
38
38
  }
39
39
  """
40
40
 
41
+ _inner_list_type = BooleanFormatterValidator
42
+
41
43
 
42
44
  class FilterIncludePlugin(Plugin[FilterIncludeOptions]):
43
45
  plugin_options_type = FilterIncludeOptions
@@ -61,8 +63,8 @@ class FilterIncludePlugin(Plugin[FilterIncludeOptions]):
61
63
  return entry
62
64
 
63
65
  for formatter in self.plugin_options.list:
64
- should_exclude = ScriptUtils.bool_formatter_output(
65
- self.overrides.apply_formatter(formatter=formatter, entry=entry)
66
+ should_exclude = self.overrides.apply_formatter(
67
+ formatter=formatter, entry=entry, expected_type=bool
66
68
  )
67
69
  if not should_exclude:
68
70
  logger.info(
@@ -140,7 +140,7 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
140
140
  if not nfo_tags:
141
141
  return
142
142
 
143
- if self.overrides.evaluate_boolean(self.plugin_options.kodi_safe):
143
+ if self.overrides.apply_formatter(self.plugin_options.kodi_safe, expected_type=bool):
144
144
  nfo_root = to_max_3_byte_utf8_string(nfo_root)
145
145
  nfo_tags = {
146
146
  to_max_3_byte_utf8_string(key): [
@@ -31,7 +31,7 @@ class SquareThumbnailPlugin(Plugin[SquareThumbnailOptions]):
31
31
 
32
32
  @property
33
33
  def _square_thumbnail(self) -> bool:
34
- return self.overrides.evaluate_boolean(self.plugin_options)
34
+ return self.overrides.apply_formatter(self.plugin_options, expected_type=bool)
35
35
 
36
36
  @classmethod
37
37
  def _convert_to_square_thumbnail(cls, entry: Entry) -> None:
@@ -42,8 +42,8 @@ class _RandomizedRangeValidator(StrictDictValidator, ABC):
42
42
  )
43
43
 
44
44
  def _randomized_float(self, overrides: Overrides, entry: Optional[Entry] = None) -> float:
45
- actualized_min = float(overrides.apply_formatter(self._min, entry=entry))
46
- actualized_max = float(overrides.apply_formatter(self._max, entry=entry))
45
+ actualized_min = overrides.apply_formatter(self._min, entry=entry, expected_type=float)
46
+ actualized_max = overrides.apply_formatter(self._max, entry=entry, expected_type=float)
47
47
 
48
48
  if actualized_min < 0:
49
49
  raise self._validation_exception(
@@ -70,7 +70,7 @@ class _RandomizedRangeValidator(StrictDictValidator, ABC):
70
70
  -------
71
71
  Max possible value
72
72
  """
73
- actualized_max = float(overrides.apply_formatter(self._max, entry=entry))
73
+ actualized_max = overrides.apply_formatter(self._max, entry=entry, expected_type=float)
74
74
  if actualized_max < 0:
75
75
  raise self._validation_exception(
76
76
  f"max must be greater than zero, received {actualized_max}"
@@ -155,8 +155,8 @@ class SubscriptionDownload(BaseSubscription, ABC):
155
155
  keep_max_files: Optional[int] = None
156
156
  if self.output_options.keep_max_files:
157
157
  # validated it can be cast to int within the validator
158
- keep_max_files = int(
159
- self.overrides.apply_formatter(self.output_options.keep_max_files)
158
+ keep_max_files = self.overrides.apply_formatter(
159
+ self.output_options.keep_max_files, expected_type=int
160
160
  )
161
161
 
162
162
  if date_range_to_keep or self.output_options.keep_max_files is not None:
@@ -94,8 +94,8 @@ class SubscriptionYTDLOptions:
94
94
  self._enhanced_download_archive.working_ytdl_file_path
95
95
  )
96
96
  if self._preset.output_options.keep_max_files:
97
- keep_max_files = int(
98
- self._overrides.apply_formatter(self._preset.output_options.keep_max_files)
97
+ keep_max_files = self._overrides.apply_formatter(
98
+ self._preset.output_options.keep_max_files, expected_type=int
99
99
  )
100
100
  if keep_max_files > 0:
101
101
  # yt-dlp has a weird bug with max_downloads=1, set to 2 for safe measure
@@ -82,35 +82,27 @@ class StringFormatterValidator(StringValidator):
82
82
  """
83
83
  return self._parsed
84
84
 
85
- def post_process(self, resolved: str) -> str:
86
- """
87
- Returns
88
- -------
89
- Apply any post processing to the resolved value
90
- """
91
- return resolved
92
-
93
- def post_process_native(self, resolved: Any) -> Any:
85
+ def post_process(self, resolved: Any) -> Any:
94
86
  """
95
87
  Returns
96
88
  -------
97
- Apply any post processing to the resolved native value.
89
+ Apply any post processing to the resolved value. Defaults to casting it to string.
98
90
  """
99
- return resolved
91
+ return str(resolved)
100
92
 
101
93
 
102
94
  class FloatFormatterValidator(StringFormatterValidator):
103
95
  _expected_value_type_name = "float"
104
96
 
105
- def post_process(self, resolved: str) -> str:
97
+ def post_process(self, resolved: str) -> float:
106
98
  try:
107
- float(resolved)
99
+ out = float(resolved)
108
100
  except Exception as exc:
109
101
  raise self._validation_exception(
110
102
  f"Expected a float, but received '{resolved}'"
111
103
  ) from exc
112
104
 
113
- return resolved
105
+ return out
114
106
 
115
107
 
116
108
  class StandardizedDateValidator(StringFormatterValidator):
@@ -127,6 +119,13 @@ class StandardizedDateValidator(StringFormatterValidator):
127
119
  return resolved
128
120
 
129
121
 
122
+ class BooleanFormatterValidator(StringFormatterValidator):
123
+ _expected_value_type_name = "boolean"
124
+
125
+ def post_process(self, resolved: Any) -> bool:
126
+ return ScriptUtils.bool_formatter_output(output=str(resolved))
127
+
128
+
130
129
  # pylint: disable=line-too-long
131
130
  class OverridesStringFormatterValidator(StringFormatterValidator):
132
131
  """
@@ -146,15 +145,14 @@ class OverridesStringFormatterValidator(StringFormatterValidator):
146
145
  class OverridesIntegerFormatterValidator(OverridesStringFormatterValidator):
147
146
  _expected_value_type_name = "integer"
148
147
 
149
- def post_process(self, resolved: str) -> str:
148
+ def post_process(self, resolved: str) -> int:
150
149
  try:
151
- int(resolved)
150
+ out = int(resolved)
152
151
  except Exception as exc:
153
152
  raise self._validation_exception(
154
153
  f"Expected an integer, but received '{resolved}'"
155
154
  ) from exc
156
-
157
- return resolved
155
+ return out
158
156
 
159
157
 
160
158
  class OverridesFloatFormatterValidator(FloatFormatterValidator, OverridesStringFormatterValidator):
@@ -163,9 +161,14 @@ class OverridesFloatFormatterValidator(FloatFormatterValidator, OverridesStringF
163
161
  """
164
162
 
165
163
 
166
- class OverridesBooleanFormatterValidator(OverridesStringFormatterValidator):
164
+ class OverridesBooleanFormatterValidator(
165
+ BooleanFormatterValidator, OverridesStringFormatterValidator
166
+ ):
167
167
  _expected_value_type_name = "boolean"
168
168
 
169
+ def post_process(self, resolved: Any) -> bool:
170
+ return ScriptUtils.bool_formatter_output(output=str(resolved))
171
+
169
172
 
170
173
  class ListFormatterValidator(ListValidator[StringFormatterValidator]):
171
174
  _inner_list_type = StringFormatterValidator
@@ -211,7 +214,22 @@ class OverridesDictFormatterValidator(DictFormatterValidator):
211
214
  _key_validator = OverridesStringFormatterValidator
212
215
 
213
216
 
217
+ class AnyFormatterValidator(StringFormatterValidator):
218
+ """
219
+ Applies no post-processing.
220
+ """
221
+
222
+ def post_process(self, resolved: Any) -> Any:
223
+ return resolved
224
+
225
+
226
+ class AnyOverridesFormatterValidator(AnyFormatterValidator, OverridesStringFormatterValidator):
227
+ pass
228
+
229
+
214
230
  class UnstructuredDictFormatterValidator(DictFormatterValidator):
231
+ _key_validator = AnyFormatterValidator
232
+
215
233
  def __init__(self, name, value):
216
234
  # Convert the unstructured-ness into a script
217
235
  if isinstance(value, dict):
@@ -220,19 +238,7 @@ class UnstructuredDictFormatterValidator(DictFormatterValidator):
220
238
 
221
239
 
222
240
  class UnstructuredOverridesDictFormatterValidator(UnstructuredDictFormatterValidator):
223
- _key_validator = OverridesStringFormatterValidator
224
-
225
-
226
- def to_variable_dependency_format_string(script: Script, parsed_format_string: SyntaxTree) -> str:
227
- """
228
- Create a dummy format string that contains all variable deps as a string.
229
- """
230
- dummy_format_string = ""
231
- for var in parsed_format_string.variables:
232
- dummy_format_string += f"{{ {var.name} }}"
233
- for variable_dependency in script._variables[var.name].variables:
234
- dummy_format_string += f"{{ {variable_dependency.name} }}"
235
- return dummy_format_string
241
+ _key_validator = AnyOverridesFormatterValidator
236
242
 
237
243
 
238
244
  def _validate_formatter(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ytdl-sub
3
- Version: 2026.1.24.post1
3
+ Version: 2026.1.27
4
4
  Summary: Automate downloading metadata generation with YoutubeDL
5
5
  Author: Jesse Bannon
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1 +0,0 @@
1
- __pypi_version__ = "2026.01.24.post1";__local_version__ = "2026.01.24+c1431c8"
File without changes