ytdl-sub 2025.12.31.post4__tar.gz → 2026.1.6__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.post4 → ytdl_sub-2026.1.6}/PKG-INFO +1 -1
  2. ytdl_sub-2026.1.6/src/ytdl_sub/__init__.py +1 -0
  3. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/overrides.py +8 -3
  4. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/validators/variable_validation.py +3 -0
  5. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/downloaders/url/downloader.py +20 -12
  6. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/downloaders/url/validators.py +14 -1
  7. ytdl_sub-2026.1.6/src/ytdl_sub/prebuilt_presets/helpers/url.yaml +177 -0
  8. ytdl_sub-2026.1.6/src/ytdl_sub/prebuilt_presets/helpers/url_categorized.yaml +111 -0
  9. ytdl_sub-2026.1.6/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_collection.yaml +1327 -0
  10. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/script.py +17 -1
  11. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/string_formatter_validators.py +9 -0
  12. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub.egg-info/PKG-INFO +1 -1
  13. ytdl_sub-2025.12.31.post4/src/ytdl_sub/__init__.py +0 -1
  14. ytdl_sub-2025.12.31.post4/src/ytdl_sub/prebuilt_presets/helpers/url.yaml +0 -1049
  15. ytdl_sub-2025.12.31.post4/src/ytdl_sub/prebuilt_presets/helpers/url_categorized.yaml +0 -210
  16. ytdl_sub-2025.12.31.post4/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_collection.yaml +0 -5693
  17. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/LICENSE +0 -0
  18. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/README.md +0 -0
  19. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/pyproject.toml +0 -0
  20. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/setup.cfg +0 -0
  21. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/cli/__init__.py +0 -0
  22. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/cli/entrypoint.py +0 -0
  23. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/cli/output_summary.py +0 -0
  24. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/cli/output_transaction_log.py +0 -0
  25. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/cli/parsers/__init__.py +0 -0
  26. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/cli/parsers/cli_to_sub.py +0 -0
  27. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/cli/parsers/dl.py +0 -0
  28. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/cli/parsers/main.py +0 -0
  29. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/__init__.py +0 -0
  30. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/config_file.py +0 -0
  31. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/config_validator.py +0 -0
  32. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/defaults.py +0 -0
  33. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/plugin/__init__.py +0 -0
  34. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/plugin/plugin.py +0 -0
  35. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/plugin/plugin_mapping.py +0 -0
  36. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/plugin/plugin_operation.py +0 -0
  37. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/plugin/preset_plugins.py +0 -0
  38. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/preset.py +0 -0
  39. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/preset_options.py +0 -0
  40. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/validators/__init__.py +0 -0
  41. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/config/validators/options.py +0 -0
  42. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/downloaders/__init__.py +0 -0
  43. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/downloaders/info_json/__init__.py +0 -0
  44. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/downloaders/info_json/info_json_downloader.py +0 -0
  45. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/downloaders/source_plugin.py +0 -0
  46. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/downloaders/url/__init__.py +0 -0
  47. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/downloaders/ytdl_options_builder.py +0 -0
  48. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/downloaders/ytdlp.py +0 -0
  49. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/__init__.py +0 -0
  50. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/base_entry.py +0 -0
  51. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/entry.py +0 -0
  52. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/entry_parent.py +0 -0
  53. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/script/__init__.py +0 -0
  54. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/script/custom_functions.py +0 -0
  55. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/script/function_scripts.py +0 -0
  56. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/script/variable_definitions.py +0 -0
  57. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/script/variable_types.py +0 -0
  58. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/variables/__init__.py +0 -0
  59. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/entries/variables/override_variables.py +0 -0
  60. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/main.py +0 -0
  61. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/__init__.py +0 -0
  62. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/audio_extract.py +0 -0
  63. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/chapters.py +0 -0
  64. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/date_range.py +0 -0
  65. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/embed_thumbnail.py +0 -0
  66. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/file_convert.py +0 -0
  67. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/filter_exclude.py +0 -0
  68. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/filter_include.py +0 -0
  69. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/format.py +0 -0
  70. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/internal/__init__.py +0 -0
  71. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/internal/view.py +0 -0
  72. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/match_filters.py +0 -0
  73. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/music_tags.py +0 -0
  74. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/nfo_tags.py +0 -0
  75. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/output_directory_nfo_tags.py +0 -0
  76. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/split_by_chapters.py +0 -0
  77. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/square_thumbnail.py +0 -0
  78. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/static_nfo_tags.py +0 -0
  79. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/subtitles.py +0 -0
  80. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/throttle_protection.py +0 -0
  81. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/plugins/video_tags.py +0 -0
  82. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/__init__.py +0 -0
  83. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/helpers/__init__.py +0 -0
  84. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/helpers/download_deletion_options.yaml +0 -0
  85. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/helpers/filter_duration.yaml +0 -0
  86. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/helpers/filter_keywords.yaml +0 -0
  87. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/helpers/media_quality.yaml +0 -0
  88. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/helpers/players.yaml +0 -0
  89. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/helpers/throttle_protection.yaml +0 -0
  90. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/helpers/url_bilateral.yaml +0 -0
  91. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/internal/__init__.py +0 -0
  92. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/internal/view.yaml +0 -0
  93. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/music/__init__.py +0 -0
  94. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/music/albums_from_chapters.yaml +0 -0
  95. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/music/albums_from_playlists.yaml +0 -0
  96. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/music/singles.yaml +0 -0
  97. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/music/soundcloud.yaml +0 -0
  98. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/music_videos/__init__.py +0 -0
  99. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_base.yaml +0 -0
  100. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_extras.yaml +0 -0
  101. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/music_videos/music_videos.yaml +0 -0
  102. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/tv_show/__init__.py +0 -0
  103. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/tv_show/episode.yaml +0 -0
  104. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show.yaml +0 -0
  105. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_by_date.yaml +0 -0
  106. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/__init__.py +0 -0
  107. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/__init__.py +0 -0
  108. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/array_functions.py +0 -0
  109. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/boolean_functions.py +0 -0
  110. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/conditional_functions.py +0 -0
  111. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/date_functions.py +0 -0
  112. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/error_functions.py +0 -0
  113. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/json_functions.py +0 -0
  114. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/map_functions.py +0 -0
  115. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/numeric_functions.py +0 -0
  116. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/print_functions.py +0 -0
  117. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/regex_functions.py +0 -0
  118. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/functions/string_functions.py +0 -0
  119. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/parser.py +0 -0
  120. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/script.py +0 -0
  121. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/script_output.py +0 -0
  122. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/types/__init__.py +0 -0
  123. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/types/array.py +0 -0
  124. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/types/function.py +0 -0
  125. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/types/map.py +0 -0
  126. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/types/resolvable.py +0 -0
  127. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/types/syntax_tree.py +0 -0
  128. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/types/variable.py +0 -0
  129. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/types/variable_dependency.py +0 -0
  130. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/utils/__init__.py +0 -0
  131. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/utils/exception_formatters.py +0 -0
  132. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/utils/exceptions.py +0 -0
  133. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/utils/name_validation.py +0 -0
  134. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/script/utils/type_checking.py +0 -0
  135. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/subscriptions/__init__.py +0 -0
  136. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/subscriptions/base_subscription.py +0 -0
  137. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/subscriptions/subscription.py +0 -0
  138. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/subscriptions/subscription_download.py +0 -0
  139. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/subscriptions/subscription_validators.py +0 -0
  140. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/subscriptions/subscription_ytdl_options.py +0 -0
  141. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/thread/__init__.py +0 -0
  142. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/thread/log_entries_downloaded_listener.py +0 -0
  143. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/__init__.py +0 -0
  144. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/chapters.py +0 -0
  145. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/datetime.py +0 -0
  146. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/exceptions.py +0 -0
  147. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/ffmpeg.py +0 -0
  148. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/file_handler.py +0 -0
  149. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/file_lock.py +0 -0
  150. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/file_path.py +0 -0
  151. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/logger.py +0 -0
  152. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/retry.py +0 -0
  153. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/scriptable.py +0 -0
  154. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/subtitles.py +0 -0
  155. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/system.py +0 -0
  156. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/thumbnail.py +0 -0
  157. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/xml.py +0 -0
  158. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/utils/yaml.py +0 -0
  159. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/__init__.py +0 -0
  160. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/audo_codec_validator.py +0 -0
  161. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/file_path_validators.py +0 -0
  162. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/nfo_validators.py +0 -0
  163. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/regex_validator.py +0 -0
  164. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/source_variable_validator.py +0 -0
  165. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/strict_dict_validator.py +0 -0
  166. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/string_datetime.py +0 -0
  167. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/string_select_validator.py +0 -0
  168. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/validators/validators.py +0 -0
  169. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/ytdl_additions/__init__.py +0 -0
  170. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub/ytdl_additions/enhanced_download_archive.py +0 -0
  171. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub.egg-info/SOURCES.txt +0 -0
  172. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub.egg-info/dependency_links.txt +0 -0
  173. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub.egg-info/entry_points.txt +0 -0
  174. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/src/ytdl_sub.egg-info/requires.txt +0 -0
  175. {ytdl_sub-2025.12.31.post4 → ytdl_sub-2026.1.6}/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.post4
3
+ Version: 2026.1.6
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.06";__local_version__ = "2026.01.06+6b99c31"
@@ -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
  )
@@ -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) }"