ytdl-sub 2025.12.31.post1__tar.gz → 2026.1.11__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 (175) hide show
  1. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/PKG-INFO +2 -2
  2. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/pyproject.toml +1 -1
  3. ytdl_sub-2026.1.11/src/ytdl_sub/__init__.py +1 -0
  4. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/overrides.py +8 -3
  5. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/validators/variable_validation.py +3 -0
  6. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/downloaders/url/downloader.py +20 -12
  7. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/downloaders/url/validators.py +14 -1
  8. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/script/variable_types.py +1 -9
  9. ytdl_sub-2026.1.11/src/ytdl_sub/prebuilt_presets/helpers/url.yaml +177 -0
  10. ytdl_sub-2026.1.11/src/ytdl_sub/prebuilt_presets/helpers/url_categorized.yaml +111 -0
  11. ytdl_sub-2026.1.11/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_collection.yaml +1327 -0
  12. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/numeric_functions.py +17 -0
  13. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/script.py +24 -0
  14. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/types/resolvable.py +8 -0
  15. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/types/variable_dependency.py +28 -12
  16. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/script.py +17 -1
  17. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/string_formatter_validators.py +14 -0
  18. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub.egg-info/PKG-INFO +2 -2
  19. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub.egg-info/requires.txt +1 -1
  20. ytdl_sub-2025.12.31.post1/src/ytdl_sub/__init__.py +0 -1
  21. ytdl_sub-2025.12.31.post1/src/ytdl_sub/prebuilt_presets/helpers/url.yaml +0 -1049
  22. ytdl_sub-2025.12.31.post1/src/ytdl_sub/prebuilt_presets/helpers/url_categorized.yaml +0 -210
  23. ytdl_sub-2025.12.31.post1/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_collection.yaml +0 -5693
  24. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/LICENSE +0 -0
  25. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/README.md +0 -0
  26. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/setup.cfg +0 -0
  27. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/cli/__init__.py +0 -0
  28. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/cli/entrypoint.py +0 -0
  29. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/cli/output_summary.py +0 -0
  30. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/cli/output_transaction_log.py +0 -0
  31. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/cli/parsers/__init__.py +0 -0
  32. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/cli/parsers/cli_to_sub.py +0 -0
  33. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/cli/parsers/dl.py +0 -0
  34. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/cli/parsers/main.py +0 -0
  35. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/__init__.py +0 -0
  36. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/config_file.py +0 -0
  37. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/config_validator.py +0 -0
  38. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/defaults.py +0 -0
  39. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/plugin/__init__.py +0 -0
  40. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/plugin/plugin.py +0 -0
  41. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/plugin/plugin_mapping.py +0 -0
  42. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/plugin/plugin_operation.py +0 -0
  43. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/plugin/preset_plugins.py +0 -0
  44. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/preset.py +0 -0
  45. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/preset_options.py +0 -0
  46. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/validators/__init__.py +0 -0
  47. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/config/validators/options.py +0 -0
  48. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/downloaders/__init__.py +0 -0
  49. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/downloaders/info_json/__init__.py +0 -0
  50. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/downloaders/info_json/info_json_downloader.py +0 -0
  51. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/downloaders/source_plugin.py +0 -0
  52. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/downloaders/url/__init__.py +0 -0
  53. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/downloaders/ytdl_options_builder.py +0 -0
  54. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/downloaders/ytdlp.py +0 -0
  55. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/__init__.py +0 -0
  56. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/base_entry.py +0 -0
  57. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/entry.py +0 -0
  58. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/entry_parent.py +0 -0
  59. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/script/__init__.py +0 -0
  60. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/script/custom_functions.py +0 -0
  61. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/script/function_scripts.py +0 -0
  62. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/script/variable_definitions.py +0 -0
  63. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/variables/__init__.py +0 -0
  64. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/entries/variables/override_variables.py +0 -0
  65. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/main.py +0 -0
  66. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/__init__.py +0 -0
  67. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/audio_extract.py +0 -0
  68. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/chapters.py +0 -0
  69. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/date_range.py +0 -0
  70. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/embed_thumbnail.py +0 -0
  71. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/file_convert.py +0 -0
  72. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/filter_exclude.py +0 -0
  73. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/filter_include.py +0 -0
  74. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/format.py +0 -0
  75. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/internal/__init__.py +0 -0
  76. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/internal/view.py +0 -0
  77. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/match_filters.py +0 -0
  78. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/music_tags.py +0 -0
  79. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/nfo_tags.py +0 -0
  80. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/output_directory_nfo_tags.py +0 -0
  81. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/split_by_chapters.py +0 -0
  82. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/square_thumbnail.py +0 -0
  83. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/static_nfo_tags.py +0 -0
  84. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/subtitles.py +0 -0
  85. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/throttle_protection.py +0 -0
  86. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/plugins/video_tags.py +0 -0
  87. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/__init__.py +0 -0
  88. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/helpers/__init__.py +0 -0
  89. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/helpers/download_deletion_options.yaml +0 -0
  90. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/helpers/filter_duration.yaml +0 -0
  91. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/helpers/filter_keywords.yaml +0 -0
  92. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/helpers/media_quality.yaml +0 -0
  93. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/helpers/players.yaml +0 -0
  94. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/helpers/throttle_protection.yaml +0 -0
  95. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/helpers/url_bilateral.yaml +0 -0
  96. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/internal/__init__.py +0 -0
  97. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/internal/view.yaml +0 -0
  98. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/music/__init__.py +0 -0
  99. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/music/albums_from_chapters.yaml +0 -0
  100. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/music/albums_from_playlists.yaml +0 -0
  101. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/music/singles.yaml +0 -0
  102. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/music/soundcloud.yaml +0 -0
  103. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/music_videos/__init__.py +0 -0
  104. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_base.yaml +0 -0
  105. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_extras.yaml +0 -0
  106. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/music_videos/music_videos.yaml +0 -0
  107. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/tv_show/__init__.py +0 -0
  108. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/tv_show/episode.yaml +0 -0
  109. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show.yaml +0 -0
  110. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_by_date.yaml +0 -0
  111. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/__init__.py +0 -0
  112. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/__init__.py +0 -0
  113. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/array_functions.py +0 -0
  114. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/boolean_functions.py +0 -0
  115. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/conditional_functions.py +0 -0
  116. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/date_functions.py +0 -0
  117. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/error_functions.py +0 -0
  118. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/json_functions.py +0 -0
  119. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/map_functions.py +0 -0
  120. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/print_functions.py +0 -0
  121. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/regex_functions.py +0 -0
  122. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/functions/string_functions.py +0 -0
  123. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/parser.py +0 -0
  124. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/script_output.py +0 -0
  125. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/types/__init__.py +0 -0
  126. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/types/array.py +0 -0
  127. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/types/function.py +0 -0
  128. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/types/map.py +0 -0
  129. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/types/syntax_tree.py +0 -0
  130. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/types/variable.py +0 -0
  131. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/utils/__init__.py +0 -0
  132. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/utils/exception_formatters.py +0 -0
  133. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/utils/exceptions.py +0 -0
  134. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/utils/name_validation.py +0 -0
  135. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/script/utils/type_checking.py +0 -0
  136. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/subscriptions/__init__.py +0 -0
  137. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/subscriptions/base_subscription.py +0 -0
  138. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/subscriptions/subscription.py +0 -0
  139. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/subscriptions/subscription_download.py +0 -0
  140. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/subscriptions/subscription_validators.py +0 -0
  141. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/subscriptions/subscription_ytdl_options.py +0 -0
  142. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/thread/__init__.py +0 -0
  143. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/thread/log_entries_downloaded_listener.py +0 -0
  144. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/__init__.py +0 -0
  145. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/chapters.py +0 -0
  146. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/datetime.py +0 -0
  147. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/exceptions.py +0 -0
  148. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/ffmpeg.py +0 -0
  149. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/file_handler.py +0 -0
  150. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/file_lock.py +0 -0
  151. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/file_path.py +0 -0
  152. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/logger.py +0 -0
  153. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/retry.py +0 -0
  154. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/scriptable.py +0 -0
  155. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/subtitles.py +0 -0
  156. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/system.py +0 -0
  157. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/thumbnail.py +0 -0
  158. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/xml.py +0 -0
  159. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/utils/yaml.py +0 -0
  160. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/__init__.py +0 -0
  161. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/audo_codec_validator.py +0 -0
  162. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/file_path_validators.py +0 -0
  163. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/nfo_validators.py +0 -0
  164. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/regex_validator.py +0 -0
  165. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/source_variable_validator.py +0 -0
  166. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/strict_dict_validator.py +0 -0
  167. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/string_datetime.py +0 -0
  168. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/string_select_validator.py +0 -0
  169. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/validators/validators.py +0 -0
  170. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/ytdl_additions/__init__.py +0 -0
  171. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub/ytdl_additions/enhanced_download_archive.py +0 -0
  172. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub.egg-info/SOURCES.txt +0 -0
  173. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub.egg-info/dependency_links.txt +0 -0
  174. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/src/ytdl_sub.egg-info/entry_points.txt +0 -0
  175. {ytdl_sub-2025.12.31.post1 → ytdl_sub-2026.1.11}/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: 2025.12.31.post1
3
+ Version: 2026.1.11
4
4
  Summary: Automate downloading metadata generation with YoutubeDL
5
5
  Author: Jesse Bannon
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -703,7 +703,7 @@ Requires-Dist: black==24.10.0; extra == "lint"
703
703
  Requires-Dist: isort==7.0.0; extra == "lint"
704
704
  Requires-Dist: pylint==4.0.1; extra == "lint"
705
705
  Provides-Extra: docs
706
- Requires-Dist: sphinx<9,>=7; extra == "docs"
706
+ Requires-Dist: sphinx<10,>=7; extra == "docs"
707
707
  Requires-Dist: sphinx-rtd-theme<4,>=2; extra == "docs"
708
708
  Requires-Dist: sphinx-book-theme~=1.0; extra == "docs"
709
709
  Requires-Dist: sphinx-copybutton~=0.5; extra == "docs"
@@ -52,7 +52,7 @@ lint = [
52
52
  "pylint==4.0.1",
53
53
  ]
54
54
  docs = [
55
- "sphinx>=7,<9",
55
+ "sphinx>=7,<10",
56
56
  "sphinx-rtd-theme>=2,<4",
57
57
  "sphinx-book-theme~=1.0",
58
58
  "sphinx-copybutton~=0.5",
@@ -0,0 +1 @@
1
+ __pypi_version__ = "2026.01.11";__local_version__ = "2026.01.11+41a5bf6"
@@ -238,20 +238,25 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable):
238
238
  def apply_overrides_formatter_to_native(
239
239
  self,
240
240
  formatter: OverridesStringFormatterValidator,
241
+ function_overrides: Optional[Dict[str, str]] = None,
241
242
  ) -> Any:
242
243
  """
243
244
  Parameters
244
245
  ----------
245
246
  formatter
246
247
  Overrides formatter to apply
248
+ function_overrides
249
+ Optional. Explicit values to override the overrides themselves and source variables
247
250
 
248
251
  Returns
249
252
  -------
250
253
  The native python form of the resolved variable
251
254
  """
252
- return self._apply_to_resolvable(
253
- formatter=formatter, entry=None, function_overrides=None
254
- ).native
255
+ return formatter.post_process_native(
256
+ self._apply_to_resolvable(
257
+ formatter=formatter, entry=None, function_overrides=function_overrides
258
+ ).native
259
+ )
255
260
 
256
261
  def evaluate_boolean(
257
262
  self, formatter: StringFormatterValidator, entry: Optional[Entry] = None
@@ -84,6 +84,9 @@ class VariableValidation:
84
84
  )
85
85
  resolved_subscription["download"] = []
86
86
  for url_output in raw_download_output["download"]:
87
+ if isinstance(url_output["url"], list):
88
+ url_output["url"] = [url for url in url_output["url"] if bool(url)]
89
+
87
90
  if url_output["url"]:
88
91
  resolved_subscription["download"].append(url_output)
89
92
 
@@ -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 self.overrides.apply_formatter(validator.url) == entry_input_url:
55
+ if entry_input_url in self.overrides.apply_overrides_formatter_to_native(validator.url):
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 self.overrides.apply_formatter(validator.url) == entry_input_url:
60
+ if entry_input_url in self.overrides.apply_overrides_formatter_to_native(validator.url):
61
61
  return validator
62
62
 
63
63
  # Return the first validator if none exist
@@ -487,19 +487,27 @@ 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 (url := self.overrides.apply_formatter(url_validator.url)):
490
+ if not (urls := self.overrides.apply_overrides_formatter_to_native(url_validator.url)):
491
491
  continue
492
492
 
493
- for entry in self._download_metadata(url=url, validator=url_validator):
494
- entry.initialize_script(self.overrides).add(
495
- {
496
- v.ytdl_sub_input_url: url,
497
- v.ytdl_sub_input_url_index: idx,
498
- v.ytdl_sub_input_url_count: len(self.collection.urls.list),
499
- }
500
- )
493
+ assert isinstance(urls, list)
494
+
495
+ for url in reversed(urls):
496
+ assert isinstance(url, str)
497
+
498
+ if not url:
499
+ continue
500
+
501
+ for entry in self._download_metadata(url=url, validator=url_validator):
502
+ entry.initialize_script(self.overrides).add(
503
+ {
504
+ v.ytdl_sub_input_url: url,
505
+ v.ytdl_sub_input_url_index: idx,
506
+ v.ytdl_sub_input_url_count: len(self.collection.urls.list),
507
+ }
508
+ )
501
509
 
502
- yield entry
510
+ yield entry
503
511
 
504
512
  def download(self, entry: Entry) -> Optional[Entry]:
505
513
  """
@@ -43,6 +43,19 @@ class UrlThumbnailListValidator(ListValidator[UrlThumbnailValidator]):
43
43
  _inner_list_type = UrlThumbnailValidator
44
44
 
45
45
 
46
+ class OverridesOneOrManyUrlValidator(OverridesStringFormatterValidator):
47
+ def post_process_native(self, resolved: Any) -> Any:
48
+ if isinstance(resolved, str):
49
+ return [resolved]
50
+ if isinstance(resolved, list):
51
+ for value in resolved:
52
+ if not isinstance(value, str):
53
+ raise self._validation_exception("Must be a string or an array of strings.")
54
+ return resolved
55
+
56
+ raise self._validation_exception("Must be a string or an array of strings.")
57
+
58
+
46
59
  class UrlValidator(StrictDictValidator):
47
60
  _required_keys = {"url"}
48
61
  _optional_keys = {
@@ -68,7 +81,7 @@ class UrlValidator(StrictDictValidator):
68
81
  super().__init__(name, value)
69
82
 
70
83
  # TODO: url validate using yt-dlp IE
71
- self._url = self._validate_key(key="url", validator=OverridesStringFormatterValidator)
84
+ self._url = self._validate_key(key="url", validator=OverridesOneOrManyUrlValidator)
72
85
  self._variables = self._validate_key_if_present(
73
86
  key="variables", validator=DictFormatterValidator, default={}
74
87
  )
@@ -22,7 +22,6 @@ VariableT = TypeVar("VariableT", bound="Variable")
22
22
 
23
23
 
24
24
  def _get(
25
- cast: str,
26
25
  metadata_variable_name: str,
27
26
  metadata_key: str,
28
27
  variable_name: Optional[str],
@@ -47,7 +46,7 @@ def _get(
47
46
  return as_type(
48
47
  variable_name=variable_name or metadata_key,
49
48
  metadata_key=metadata_key,
50
- definition=f"{{ %legacy_bracket_safety(%{cast}({out})) }}",
49
+ definition=f"{{ {out} }}",
51
50
  )
52
51
 
53
52
 
@@ -182,7 +181,6 @@ class MapMetadataVariable(MetadataVariable, MapVariable):
182
181
  Creates a map variable from entry metadata
183
182
  """
184
183
  return _get(
185
- "map",
186
184
  metadata_variable_name=ENTRY_METADATA_VARIABLE_NAME,
187
185
  metadata_key=metadata_key,
188
186
  variable_name=variable_name,
@@ -204,7 +202,6 @@ class ArrayMetadataVariable(MetadataVariable, ArrayVariable):
204
202
  Creates an array variable from entry metadata
205
203
  """
206
204
  return _get(
207
- "array",
208
205
  metadata_variable_name=ENTRY_METADATA_VARIABLE_NAME,
209
206
  metadata_key=metadata_key,
210
207
  variable_name=variable_name,
@@ -226,7 +223,6 @@ class StringMetadataVariable(MetadataVariable, StringVariable):
226
223
  Creates a string variable from entry metadata
227
224
  """
228
225
  return _get(
229
- "string",
230
226
  metadata_variable_name=ENTRY_METADATA_VARIABLE_NAME,
231
227
  metadata_key=metadata_key,
232
228
  variable_name=variable_name,
@@ -245,7 +241,6 @@ class StringMetadataVariable(MetadataVariable, StringVariable):
245
241
  Creates a string variable from playlist metadata
246
242
  """
247
243
  return _get(
248
- "string",
249
244
  metadata_variable_name=PLAYLIST_METADATA_VARIABLE_NAME,
250
245
  metadata_key=metadata_key,
251
246
  variable_name=variable_name,
@@ -264,7 +259,6 @@ class StringMetadataVariable(MetadataVariable, StringVariable):
264
259
  Creates a string variable from source metadata
265
260
  """
266
261
  return _get(
267
- "string",
268
262
  metadata_variable_name=SOURCE_METADATA_VARIABLE_NAME,
269
263
  metadata_key=metadata_key,
270
264
  variable_name=variable_name,
@@ -301,7 +295,6 @@ class IntegerMetadataVariable(MetadataVariable, IntegerVariable):
301
295
  Creates an int variable from entry metadata
302
296
  """
303
297
  return _get(
304
- "int",
305
298
  metadata_variable_name=ENTRY_METADATA_VARIABLE_NAME,
306
299
  metadata_key=metadata_key,
307
300
  variable_name=variable_name,
@@ -320,7 +313,6 @@ class IntegerMetadataVariable(MetadataVariable, IntegerVariable):
320
313
  Creates an int variable from playlist metadata
321
314
  """
322
315
  return _get(
323
- "int",
324
316
  metadata_variable_name=PLAYLIST_METADATA_VARIABLE_NAME,
325
317
  metadata_key=metadata_key,
326
318
  variable_name=variable_name,
@@ -0,0 +1,177 @@
1
+ presets:
2
+
3
+ ##################################################################################
4
+ # Multi urls in the form of
5
+ #
6
+ # "Subscription Name":
7
+ # - "url"
8
+ # - "url2"
9
+ # or
10
+ #
11
+ # "Subscription Name": "url"
12
+ #
13
+ # where the first url tries to grab channel avatar + banner
14
+ #
15
+ _multi_url:
16
+ download:
17
+ - url: "{ %array_at(urls, 0) }"
18
+ playlist_thumbnails:
19
+ - name: "{avatar_uncropped_thumbnail_file_name}"
20
+ uid: "avatar_uncropped"
21
+ - name: "{banner_uncropped_thumbnail_file_name}"
22
+ uid: "banner_uncropped"
23
+ source_thumbnails:
24
+ - name: "{avatar_uncropped_thumbnail_file_name}"
25
+ uid: "avatar_uncropped"
26
+ - name: "{banner_uncropped_thumbnail_file_name}"
27
+ uid: "banner_uncropped"
28
+ include_sibling_metadata: "{include_sibling_metadata}"
29
+ webpage_url: "{modified_webpage_url}"
30
+
31
+ - url: "{ %array_slice(urls, 1) }"
32
+ include_sibling_metadata: "{include_sibling_metadata}"
33
+ webpage_url: "{modified_webpage_url}"
34
+ overrides:
35
+ avatar_uncropped_thumbnail_file_name: ""
36
+ banner_uncropped_thumbnail_file_name: ""
37
+ include_sibling_metadata: False
38
+ modified_webpage_url: "{webpage_url}"
39
+
40
+ subscription_array: >-
41
+ {
42
+ [
43
+ url, url2, url3, url4, url5, url6, url7, url8, url9, url10,
44
+ url11, url12, url13, url14, url15, url16, url17, url18, url19, url20,
45
+ url21, url22, url23, url24, url25, url26, url27, url28, url29, url30,
46
+ url31, url32, url33, url34, url35, url36, url37, url38, url39, url40,
47
+ url41, url42, url43, url44, url45, url46, url47, url48, url49, url50,
48
+ url51, url52, url53, url54, url55, url56, url57, url58, url59, url60,
49
+ url61, url62, url63, url64, url65, url66, url67, url68, url69, url70,
50
+ url71, url72, url73, url74, url75, url76, url77, url78, url79, url80,
51
+ url81, url82, url83, url84, url85, url86, url87, url88, url89, url90,
52
+ url91, url92, url93, url94, url95, url96, url97, url98, url99, url100
53
+ ]
54
+ }
55
+ urls: "{subscription_array}"
56
+
57
+ subscription_value: ""
58
+ url: "{subscription_value}"
59
+ url2: ""
60
+ url3: ""
61
+ url4: ""
62
+ url5: ""
63
+ url6: ""
64
+ url7: ""
65
+ url8: ""
66
+ url9: ""
67
+ url10: ""
68
+ url11: ""
69
+ url12: ""
70
+ url13: ""
71
+ url14: ""
72
+ url15: ""
73
+ url16: ""
74
+ url17: ""
75
+ url18: ""
76
+ url19: ""
77
+ url20: ""
78
+ url21: ""
79
+ url22: ""
80
+ url23: ""
81
+ url24: ""
82
+ url25: ""
83
+ url26: ""
84
+ url27: ""
85
+ url28: ""
86
+ url29: ""
87
+ url30: ""
88
+ url31: ""
89
+ url32: ""
90
+ url33: ""
91
+ url34: ""
92
+ url35: ""
93
+ url36: ""
94
+ url37: ""
95
+ url38: ""
96
+ url39: ""
97
+ url40: ""
98
+ url41: ""
99
+ url42: ""
100
+ url43: ""
101
+ url44: ""
102
+ url45: ""
103
+ url46: ""
104
+ url47: ""
105
+ url48: ""
106
+ url49: ""
107
+ url50: ""
108
+ url51: ""
109
+ url52: ""
110
+ url53: ""
111
+ url54: ""
112
+ url55: ""
113
+ url56: ""
114
+ url57: ""
115
+ url58: ""
116
+ url59: ""
117
+ url60: ""
118
+ url61: ""
119
+ url62: ""
120
+ url63: ""
121
+ url64: ""
122
+ url65: ""
123
+ url66: ""
124
+ url67: ""
125
+ url68: ""
126
+ url69: ""
127
+ url70: ""
128
+ url71: ""
129
+ url72: ""
130
+ url73: ""
131
+ url74: ""
132
+ url75: ""
133
+ url76: ""
134
+ url77: ""
135
+ url78: ""
136
+ url79: ""
137
+ url80: ""
138
+ url81: ""
139
+ url82: ""
140
+ url83: ""
141
+ url84: ""
142
+ url85: ""
143
+ url86: ""
144
+ url87: ""
145
+ url88: ""
146
+ url89: ""
147
+ url90: ""
148
+ url91: ""
149
+ url92: ""
150
+ url93: ""
151
+ url94: ""
152
+ url95: ""
153
+ url96: ""
154
+ url97: ""
155
+ url98: ""
156
+ url99: ""
157
+ url100: ""
158
+
159
+
160
+ # multi-url with bilateral scraping built into it via
161
+ # inspection of the URL and conditionally adding another
162
+ # ytdl-sub url download with the scraping and download reversed
163
+ _multi_url_bilateral_inner:
164
+ preset:
165
+ - "_url_bilateral_overrides"
166
+
167
+ download:
168
+ - url: "{ %array_apply(urls, %bilateral_url) }"
169
+ download_reverse: False
170
+ ytdl_options:
171
+ playlist_items: "-1:0:-1"
172
+ webpage_url: "{modified_webpage_url}"
173
+
174
+ _multi_url_bilateral:
175
+ preset:
176
+ - "_multi_url_bilateral_inner"
177
+ - "_multi_url"
@@ -0,0 +1,111 @@
1
+ presets:
2
+ # Preset for categorizing URls like so
3
+ #
4
+ # subscription_name:
5
+ # category_1:
6
+ # - https://...1
7
+ # - https://...2
8
+ # category_2:
9
+ # - url: https://...1
10
+ # metadata_field_1: 2011
11
+ # metadata_field_2: 'abc"
12
+ # ...
13
+ #
14
+ # Metadata field of `url` must exist, other ones can be anything.
15
+ # Only supports a single level of nesting.
16
+ _url_categorized:
17
+ preset:
18
+ - "_multi_url_bilateral"
19
+
20
+ overrides:
21
+ # Parameters:
22
+ # $0: url string or map containing url + metadata fields
23
+ # $1: category key
24
+ #
25
+ # Output:
26
+ # Map containing { url: ..., category: ..., metadata_field_1: ... }
27
+ "%flat_array__url_to_map_format": >-
28
+ {
29
+ %elif(
30
+ %is_map($0),
31
+ %map_extend( $0, { "category": $1 } ),
32
+ %is_string($0),
33
+ { "url": $0, "category": $1 },
34
+ %throw("If using album categories, each URL must be either a string or map with metadata fields")
35
+ )
36
+ }
37
+
38
+ # Parameters:
39
+ # $0: category key
40
+ # $1: array of URLs in either string or map form
41
+ #
42
+ # Output:
43
+ # Array containing [{ url: ..., category: ... }, ... ]
44
+ "%flat_array__category_to_map_format": >-
45
+ {
46
+ %array_apply_fixed(
47
+ %assert_then(
48
+ %is_array( $1 ),
49
+ $1,
50
+ "If using album categories, each category must map to an array"
51
+ ),
52
+ $0,
53
+ %flat_array__url_to_map_format
54
+ )
55
+ }
56
+
57
+ # Parameters:
58
+ # $0: map containing fields { url: <url_value>, category: ... }
59
+ #
60
+ # Output:
61
+ # Nested map containing { <url_value>: { url: <url_value>, category: ... } }
62
+ "%flat_array__url_keyed_map_format": >-
63
+ { { %map_get($0, "url"): $0 } }
64
+
65
+ # Creates an array in the form of [ { url: ..., category: ..., metadata_field_1: ... }, ... ]
66
+ category_url_array: >-
67
+ { %array_flatten( %map_apply( subscription_map, %flat_array__category_to_map_format ) ) }
68
+
69
+ # Creates a map in the form of { <url value>: { category: ..., metadata_field_1: ... }, ... }
70
+ category_url_map: >-
71
+ {
72
+ %array_reduce(
73
+ %array_apply( category_url_array, %flat_array__url_keyed_map_format ),
74
+ %map_extend
75
+ )
76
+ }
77
+
78
+ # Parameters:
79
+ # $0: metadata field to fetch for the current url
80
+ # $1: default value
81
+ #
82
+ # Output:
83
+ # Metadata field value if it exists. Otherwise, return default value
84
+ "%get_url_field": >-
85
+ { %map_get( %map( %map_get( category_url_map, ytdl_sub_input_url, {} ) ), $0, $1 ) }
86
+
87
+ # Parameters:
88
+ # $0: metadata field to fetch for the current url
89
+ #
90
+ # Output:
91
+ # True if field is specified. False otherwise.
92
+ "%contains_url_field": >-
93
+ { %not( %is_null( %get_url_field( $0, null ) ) ) }
94
+
95
+
96
+ # Parameters:
97
+ # $0: integer to fetch the i'th url using 1-based indexing
98
+ #
99
+ # Output:
100
+ # i'th url in the category map
101
+ "%get_url_i": >-
102
+ {
103
+ %map_get(
104
+ %map( %array_at( category_url_array, %int( %sub($0, 1) ), {} ) ),
105
+ "url",
106
+ %array_at(subscription_array, %int( %sub($0, 1) ), null)
107
+ )
108
+ }
109
+
110
+ subscription_map: "{ {} }"
111
+ urls: "{ %array_apply(%range(100, 1), %get_url_i) }"