ytdl-sub 2026.1.6__tar.gz → 2026.1.13__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.
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/PKG-INFO +2 -2
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/pyproject.toml +1 -1
- ytdl_sub-2026.1.13/src/ytdl_sub/__init__.py +1 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/variable_types.py +1 -9
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/script.py +59 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/array.py +22 -1
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/function.py +178 -1
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/map.py +30 -1
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/syntax_tree.py +30 -1
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/variable_dependency.py +66 -3
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/script.py +37 -2
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/PKG-INFO +2 -2
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/requires.txt +1 -1
- ytdl_sub-2026.1.6/src/ytdl_sub/__init__.py +0 -1
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/LICENSE +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/README.md +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/setup.cfg +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/entrypoint.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/output_summary.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/output_transaction_log.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/cli_to_sub.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/dl.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/main.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/config_file.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/config_validator.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/defaults.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/overrides.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/plugin.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/plugin_mapping.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/plugin_operation.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/preset_plugins.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/preset.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/preset_options.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/validators/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/validators/options.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/validators/variable_validation.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/info_json/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/info_json/info_json_downloader.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/source_plugin.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/url/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/url/downloader.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/url/validators.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/ytdl_options_builder.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/ytdlp.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/base_entry.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/entry.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/entry_parent.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/custom_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/function_scripts.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/variable_definitions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/variables/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/variables/override_variables.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/main.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/audio_extract.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/chapters.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/date_range.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/embed_thumbnail.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/file_convert.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/filter_exclude.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/filter_include.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/format.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/internal/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/internal/view.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/match_filters.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/music_tags.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/nfo_tags.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/output_directory_nfo_tags.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/split_by_chapters.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/square_thumbnail.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/static_nfo_tags.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/subtitles.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/throttle_protection.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/video_tags.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/download_deletion_options.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/filter_duration.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/filter_keywords.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/media_quality.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/players.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/throttle_protection.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/url.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/url_bilateral.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/url_categorized.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/internal/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/internal/view.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/albums_from_chapters.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/albums_from_playlists.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/singles.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/soundcloud.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_base.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_extras.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/music_videos.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/episode.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_by_date.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_collection.yaml +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/array_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/boolean_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/conditional_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/date_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/error_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/json_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/map_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/numeric_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/print_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/regex_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/string_functions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/parser.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/script_output.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/resolvable.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/variable.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/exception_formatters.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/exceptions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/name_validation.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/type_checking.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/base_subscription.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription_download.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription_validators.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription_ytdl_options.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/thread/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/thread/log_entries_downloaded_listener.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/chapters.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/datetime.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/exceptions.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/ffmpeg.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/file_handler.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/file_lock.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/file_path.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/logger.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/retry.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/scriptable.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/subtitles.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/system.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/thumbnail.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/xml.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/yaml.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/audo_codec_validator.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/file_path_validators.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/nfo_validators.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/regex_validator.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/source_variable_validator.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/strict_dict_validator.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/string_datetime.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/string_formatter_validators.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/string_select_validator.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/validators.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/ytdl_additions/__init__.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/ytdl_additions/enhanced_download_archive.py +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/SOURCES.txt +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/dependency_links.txt +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/entry_points.txt +0 -0
- {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/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.
|
|
3
|
+
Version: 2026.1.13
|
|
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<
|
|
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"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__pypi_version__ = "2026.01.13";__local_version__ = "2026.01.13+530d2f7"
|
|
@@ -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"{{
|
|
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,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# pylint: disable=missing-raises-doc
|
|
2
|
+
import copy
|
|
2
3
|
from typing import Dict
|
|
3
4
|
from typing import List
|
|
4
5
|
from typing import Optional
|
|
@@ -7,6 +8,7 @@ from typing import Set
|
|
|
7
8
|
from ytdl_sub.script.functions import Functions
|
|
8
9
|
from ytdl_sub.script.parser import parse
|
|
9
10
|
from ytdl_sub.script.script_output import ScriptOutput
|
|
11
|
+
from ytdl_sub.script.types.resolvable import Argument
|
|
10
12
|
from ytdl_sub.script.types.resolvable import BuiltInFunctionType
|
|
11
13
|
from ytdl_sub.script.types.resolvable import Lambda
|
|
12
14
|
from ytdl_sub.script.types.resolvable import Resolvable
|
|
@@ -14,6 +16,7 @@ from ytdl_sub.script.types.syntax_tree import ResolvedSyntaxTree
|
|
|
14
16
|
from ytdl_sub.script.types.syntax_tree import SyntaxTree
|
|
15
17
|
from ytdl_sub.script.types.variable import FunctionArgument
|
|
16
18
|
from ytdl_sub.script.types.variable import Variable
|
|
19
|
+
from ytdl_sub.script.types.variable_dependency import VariableDependency
|
|
17
20
|
from ytdl_sub.script.utils.exceptions import UNREACHABLE
|
|
18
21
|
from ytdl_sub.script.utils.exceptions import CycleDetected
|
|
19
22
|
from ytdl_sub.script.utils.exceptions import IncompatibleFunctionArguments
|
|
@@ -695,3 +698,59 @@ class Script:
|
|
|
695
698
|
Names of all functions within the Script.
|
|
696
699
|
"""
|
|
697
700
|
return set(to_function_definition_name(name) for name in self._functions.keys())
|
|
701
|
+
|
|
702
|
+
def resolve_partial(
|
|
703
|
+
self,
|
|
704
|
+
unresolvable: Optional[Set[str]] = None,
|
|
705
|
+
) -> "Script":
|
|
706
|
+
"""
|
|
707
|
+
Returns
|
|
708
|
+
-------
|
|
709
|
+
New (deep-copied) script that resolves inner variables as much
|
|
710
|
+
as possible.
|
|
711
|
+
"""
|
|
712
|
+
unresolvable: Set[str] = unresolvable or {}
|
|
713
|
+
resolved: Dict[Variable, Resolvable] = {}
|
|
714
|
+
unresolved: Dict[Variable, Argument] = {
|
|
715
|
+
Variable(name): definition
|
|
716
|
+
for name, definition in self._variables.items()
|
|
717
|
+
if name not in unresolvable
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
partially_resolved = True
|
|
721
|
+
while partially_resolved:
|
|
722
|
+
|
|
723
|
+
partially_resolved = False
|
|
724
|
+
|
|
725
|
+
for variable in list(unresolved.keys()):
|
|
726
|
+
definition = unresolved[variable]
|
|
727
|
+
|
|
728
|
+
maybe_resolved = definition
|
|
729
|
+
if isinstance(definition, Variable) and definition.name not in unresolvable:
|
|
730
|
+
maybe_resolved = resolved.get(definition, unresolved[definition])
|
|
731
|
+
elif isinstance(definition, VariableDependency):
|
|
732
|
+
maybe_resolved = definition.partial_resolve(
|
|
733
|
+
resolved_variables=resolved,
|
|
734
|
+
unresolved_variables=unresolved,
|
|
735
|
+
custom_functions=self._functions,
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
if isinstance(maybe_resolved, Resolvable):
|
|
739
|
+
resolved[variable] = maybe_resolved
|
|
740
|
+
del unresolved[variable]
|
|
741
|
+
partially_resolved = True
|
|
742
|
+
else:
|
|
743
|
+
unresolved[variable] = maybe_resolved
|
|
744
|
+
|
|
745
|
+
# If the definition changed, then the script changed
|
|
746
|
+
# which means we can iterate again
|
|
747
|
+
partially_resolved |= definition != maybe_resolved
|
|
748
|
+
|
|
749
|
+
return copy.deepcopy(self).add_parsed(
|
|
750
|
+
{var_name: self._variables[var_name] for var_name in unresolvable}
|
|
751
|
+
| {
|
|
752
|
+
var.name: ResolvedSyntaxTree(ast=[definition])
|
|
753
|
+
for var, definition in resolved.items()
|
|
754
|
+
}
|
|
755
|
+
| {var.name: SyntaxTree(ast=[definition]) for var, definition in unresolved.items()}
|
|
756
|
+
)
|
|
@@ -28,7 +28,7 @@ class UnresolvedArray(_Array, VariableDependency, FutureResolvable):
|
|
|
28
28
|
value: List[Argument]
|
|
29
29
|
|
|
30
30
|
@property
|
|
31
|
-
def
|
|
31
|
+
def iterable_arguments(self) -> List[Argument]:
|
|
32
32
|
return self.value
|
|
33
33
|
|
|
34
34
|
def resolve(
|
|
@@ -47,6 +47,27 @@ class UnresolvedArray(_Array, VariableDependency, FutureResolvable):
|
|
|
47
47
|
]
|
|
48
48
|
)
|
|
49
49
|
|
|
50
|
+
def partial_resolve(
|
|
51
|
+
self,
|
|
52
|
+
resolved_variables: Dict[Variable, Resolvable],
|
|
53
|
+
unresolved_variables: Dict[Variable, Argument],
|
|
54
|
+
custom_functions: Dict[str, VariableDependency],
|
|
55
|
+
) -> Argument | Resolvable:
|
|
56
|
+
maybe_resolvable_values, is_resolvable = VariableDependency.try_partial_resolve(
|
|
57
|
+
args=self.value,
|
|
58
|
+
resolved_variables=resolved_variables,
|
|
59
|
+
unresolved_variables=unresolved_variables,
|
|
60
|
+
custom_functions=custom_functions,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
out = UnresolvedArray(value=maybe_resolvable_values)
|
|
64
|
+
if is_resolvable:
|
|
65
|
+
return out.resolve(
|
|
66
|
+
resolved_variables=resolved_variables, custom_functions=custom_functions
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return out
|
|
70
|
+
|
|
50
71
|
def future_resolvable_type(self) -> Type[Resolvable]:
|
|
51
72
|
return Array
|
|
52
73
|
|
|
@@ -4,6 +4,7 @@ from dataclasses import dataclass
|
|
|
4
4
|
from typing import Callable
|
|
5
5
|
from typing import Dict
|
|
6
6
|
from typing import List
|
|
7
|
+
from typing import Optional
|
|
7
8
|
from typing import Type
|
|
8
9
|
from typing import Union
|
|
9
10
|
|
|
@@ -11,9 +12,11 @@ from ytdl_sub.script.functions import Functions
|
|
|
11
12
|
from ytdl_sub.script.types.array import Array
|
|
12
13
|
from ytdl_sub.script.types.array import UnresolvedArray
|
|
13
14
|
from ytdl_sub.script.types.resolvable import Argument
|
|
15
|
+
from ytdl_sub.script.types.resolvable import Boolean
|
|
14
16
|
from ytdl_sub.script.types.resolvable import BuiltInFunctionType
|
|
15
17
|
from ytdl_sub.script.types.resolvable import FunctionType
|
|
16
18
|
from ytdl_sub.script.types.resolvable import FutureResolvable
|
|
19
|
+
from ytdl_sub.script.types.resolvable import Integer
|
|
17
20
|
from ytdl_sub.script.types.resolvable import Lambda
|
|
18
21
|
from ytdl_sub.script.types.resolvable import NamedCustomFunction
|
|
19
22
|
from ytdl_sub.script.types.resolvable import Resolvable
|
|
@@ -35,7 +38,7 @@ from ytdl_sub.script.utils.type_checking import is_union
|
|
|
35
38
|
@dataclass(frozen=True)
|
|
36
39
|
class Function(FunctionType, VariableDependency, ABC):
|
|
37
40
|
@property
|
|
38
|
-
def
|
|
41
|
+
def iterable_arguments(self) -> List[Argument]:
|
|
39
42
|
return self.args
|
|
40
43
|
|
|
41
44
|
|
|
@@ -84,6 +87,52 @@ class CustomFunction(Function, NamedCustomFunction):
|
|
|
84
87
|
# been checked in the parser with
|
|
85
88
|
raise UNREACHABLE
|
|
86
89
|
|
|
90
|
+
def partial_resolve(
|
|
91
|
+
self,
|
|
92
|
+
resolved_variables: Dict[Variable, Resolvable],
|
|
93
|
+
unresolved_variables: Dict[Variable, Argument],
|
|
94
|
+
custom_functions: Dict[str, VariableDependency],
|
|
95
|
+
) -> Argument | Resolvable:
|
|
96
|
+
maybe_resolvable_args, _ = VariableDependency.try_partial_resolve(
|
|
97
|
+
args=self.args,
|
|
98
|
+
resolved_variables=resolved_variables,
|
|
99
|
+
unresolved_variables=unresolved_variables,
|
|
100
|
+
custom_functions=custom_functions,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
for i in range(len(self.args)):
|
|
104
|
+
function_arg = FunctionArgument.from_idx(idx=i, custom_function_name=self.name)
|
|
105
|
+
function_value = maybe_resolvable_args[i]
|
|
106
|
+
|
|
107
|
+
if isinstance(function_value, Resolvable):
|
|
108
|
+
resolved_variables[function_arg] = function_value
|
|
109
|
+
else:
|
|
110
|
+
unresolved_variables[function_arg] = function_value
|
|
111
|
+
|
|
112
|
+
assert len(custom_functions[self.name].iterable_arguments) == 1
|
|
113
|
+
custom_function_definition = custom_functions[self.name].iterable_arguments[0]
|
|
114
|
+
|
|
115
|
+
maybe_resolvable_custom_function, is_resolvable = VariableDependency.try_partial_resolve(
|
|
116
|
+
args=[custom_function_definition],
|
|
117
|
+
resolved_variables=resolved_variables,
|
|
118
|
+
unresolved_variables=unresolved_variables,
|
|
119
|
+
custom_functions=custom_functions,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
for i in range(len(self.args)):
|
|
123
|
+
function_arg = FunctionArgument.from_idx(idx=i, custom_function_name=self.name)
|
|
124
|
+
|
|
125
|
+
if isinstance(maybe_resolvable_args[i], Resolvable):
|
|
126
|
+
del resolved_variables[function_arg]
|
|
127
|
+
else:
|
|
128
|
+
del unresolved_variables[function_arg]
|
|
129
|
+
|
|
130
|
+
if is_resolvable:
|
|
131
|
+
return maybe_resolvable_custom_function[0]
|
|
132
|
+
|
|
133
|
+
# Did not resolve custom function arguments, do not proceed
|
|
134
|
+
return CustomFunction(name=self.name, args=maybe_resolvable_args)
|
|
135
|
+
|
|
87
136
|
|
|
88
137
|
class BuiltInFunction(Function, BuiltInFunctionType):
|
|
89
138
|
def validate_args(self) -> "BuiltInFunction":
|
|
@@ -312,5 +361,133 @@ class BuiltInFunction(Function, BuiltInFunctionType):
|
|
|
312
361
|
f"Runtime error occurred when executing the function %{self.name}: {str(exc)}"
|
|
313
362
|
) from exc
|
|
314
363
|
|
|
364
|
+
def _partial_resolve_conditional(
|
|
365
|
+
self,
|
|
366
|
+
resolved_variables: Dict[Variable, Resolvable],
|
|
367
|
+
unresolved_variables: Dict[Variable, Argument],
|
|
368
|
+
custom_functions: Dict[str, "VariableDependency"],
|
|
369
|
+
):
|
|
370
|
+
"""
|
|
371
|
+
If the conditional partially resolvable enough to warrant evaluation,
|
|
372
|
+
perform it here.
|
|
373
|
+
"""
|
|
374
|
+
if self.is_subset_of(
|
|
375
|
+
variables=resolved_variables, custom_function_definitions=custom_functions
|
|
376
|
+
):
|
|
377
|
+
return self.resolve(
|
|
378
|
+
resolved_variables=resolved_variables,
|
|
379
|
+
custom_functions=custom_functions,
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
if self.name == "if":
|
|
383
|
+
maybe_resolvable_arg, is_resolvable = VariableDependency.try_partial_resolve(
|
|
384
|
+
args=[self.args[0]],
|
|
385
|
+
resolved_variables=resolved_variables,
|
|
386
|
+
unresolved_variables=unresolved_variables,
|
|
387
|
+
custom_functions=custom_functions,
|
|
388
|
+
)
|
|
389
|
+
if is_resolvable:
|
|
390
|
+
boolean_output = maybe_resolvable_arg[0]
|
|
391
|
+
assert isinstance(boolean_output, Boolean)
|
|
392
|
+
return self.args[1] if boolean_output.native else self.args[2]
|
|
393
|
+
|
|
394
|
+
if self.name == "elif":
|
|
395
|
+
for idx in range(0, len(self.args), 2):
|
|
396
|
+
maybe_resolvable_arg, is_resolvable = VariableDependency.try_partial_resolve(
|
|
397
|
+
args=[self.args[idx]],
|
|
398
|
+
resolved_variables=resolved_variables,
|
|
399
|
+
unresolved_variables=unresolved_variables,
|
|
400
|
+
custom_functions=custom_functions,
|
|
401
|
+
)
|
|
402
|
+
if is_resolvable:
|
|
403
|
+
boolean_output = maybe_resolvable_arg[0]
|
|
404
|
+
assert isinstance(boolean_output, Boolean)
|
|
405
|
+
if boolean_output.native:
|
|
406
|
+
return self.args[idx + 1]
|
|
407
|
+
else:
|
|
408
|
+
break
|
|
409
|
+
|
|
410
|
+
if self.name == "assert_then":
|
|
411
|
+
maybe_resolvable_arg, is_resolvable = VariableDependency.try_partial_resolve(
|
|
412
|
+
args=[self.args[0]],
|
|
413
|
+
resolved_variables=resolved_variables,
|
|
414
|
+
unresolved_variables=unresolved_variables,
|
|
415
|
+
custom_functions=custom_functions,
|
|
416
|
+
)
|
|
417
|
+
if is_resolvable:
|
|
418
|
+
boolean_output = maybe_resolvable_arg[0]
|
|
419
|
+
assert isinstance(boolean_output, Boolean)
|
|
420
|
+
if boolean_output.native:
|
|
421
|
+
return self.args[1]
|
|
422
|
+
|
|
423
|
+
return self
|
|
424
|
+
|
|
425
|
+
def _try_optimized_partial_resolve(
|
|
426
|
+
self,
|
|
427
|
+
resolved_variables: Dict[Variable, Resolvable],
|
|
428
|
+
unresolved_variables: Dict[Variable, Argument],
|
|
429
|
+
custom_functions: Dict[str, "VariableDependency"],
|
|
430
|
+
) -> Optional[Argument]:
|
|
431
|
+
"""
|
|
432
|
+
If a function has enough (but not all) resolved parameters to warrant a return,
|
|
433
|
+
perform it here.
|
|
434
|
+
"""
|
|
435
|
+
if self.name == "array_at":
|
|
436
|
+
if (
|
|
437
|
+
isinstance(self.args[0], UnresolvedArray)
|
|
438
|
+
and isinstance(self.args[1], Integer)
|
|
439
|
+
and len(self.args[0].value) >= self.args[1].value
|
|
440
|
+
):
|
|
441
|
+
maybe_resolvable_values, _ = VariableDependency.try_partial_resolve(
|
|
442
|
+
args=[self.args[0].value[self.args[1].value]],
|
|
443
|
+
resolved_variables=resolved_variables,
|
|
444
|
+
unresolved_variables=unresolved_variables,
|
|
445
|
+
custom_functions=custom_functions,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
return maybe_resolvable_values[0]
|
|
449
|
+
|
|
450
|
+
return None
|
|
451
|
+
|
|
452
|
+
def partial_resolve(
|
|
453
|
+
self,
|
|
454
|
+
resolved_variables: Dict[Variable, Resolvable],
|
|
455
|
+
unresolved_variables: Dict[Variable, Argument],
|
|
456
|
+
custom_functions: Dict[str, VariableDependency],
|
|
457
|
+
) -> Argument | Resolvable:
|
|
458
|
+
conditional_return_args = self.function_spec.conditional_arg_indices(
|
|
459
|
+
num_input_args=len(self.args)
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
if conditional_return_args:
|
|
463
|
+
return self._partial_resolve_conditional(
|
|
464
|
+
resolved_variables=resolved_variables,
|
|
465
|
+
unresolved_variables=unresolved_variables,
|
|
466
|
+
custom_functions=custom_functions,
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
if partial_resolved := self._try_optimized_partial_resolve(
|
|
470
|
+
resolved_variables=resolved_variables,
|
|
471
|
+
unresolved_variables=unresolved_variables,
|
|
472
|
+
custom_functions=custom_functions,
|
|
473
|
+
):
|
|
474
|
+
return partial_resolved
|
|
475
|
+
|
|
476
|
+
maybe_resolvable_values, is_resolvable = VariableDependency.try_partial_resolve(
|
|
477
|
+
args=self.args,
|
|
478
|
+
resolved_variables=resolved_variables,
|
|
479
|
+
unresolved_variables=unresolved_variables,
|
|
480
|
+
custom_functions=custom_functions,
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
out = BuiltInFunction(name=self.name, args=maybe_resolvable_values)
|
|
484
|
+
if is_resolvable:
|
|
485
|
+
return out.resolve(
|
|
486
|
+
resolved_variables=resolved_variables,
|
|
487
|
+
custom_functions=custom_functions,
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
return out
|
|
491
|
+
|
|
315
492
|
def __hash__(self):
|
|
316
493
|
return hash((self.name, *self.args))
|
|
@@ -31,7 +31,7 @@ class UnresolvedMap(_Map, VariableDependency, FutureResolvable):
|
|
|
31
31
|
value: Dict[Argument, Argument]
|
|
32
32
|
|
|
33
33
|
@property
|
|
34
|
-
def
|
|
34
|
+
def iterable_arguments(self) -> List[Argument]:
|
|
35
35
|
return list(itertools.chain(*self.value.items()))
|
|
36
36
|
|
|
37
37
|
def resolve(
|
|
@@ -55,6 +55,35 @@ class UnresolvedMap(_Map, VariableDependency, FutureResolvable):
|
|
|
55
55
|
|
|
56
56
|
return Map(output)
|
|
57
57
|
|
|
58
|
+
def partial_resolve(
|
|
59
|
+
self,
|
|
60
|
+
resolved_variables: Dict[Variable, Resolvable],
|
|
61
|
+
unresolved_variables: Dict[Variable, Argument],
|
|
62
|
+
custom_functions: Dict[str, VariableDependency],
|
|
63
|
+
) -> Argument | Resolvable:
|
|
64
|
+
maybe_resolvable_keys, is_keys_resolvable = VariableDependency.try_partial_resolve(
|
|
65
|
+
args=self.value.keys(),
|
|
66
|
+
resolved_variables=resolved_variables,
|
|
67
|
+
unresolved_variables=unresolved_variables,
|
|
68
|
+
custom_functions=custom_functions,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
maybe_resolvable_values, is_values_resolvable = VariableDependency.try_partial_resolve(
|
|
72
|
+
args=self.value.values(),
|
|
73
|
+
resolved_variables=resolved_variables,
|
|
74
|
+
unresolved_variables=unresolved_variables,
|
|
75
|
+
custom_functions=custom_functions,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
out = UnresolvedMap(value=dict(zip(maybe_resolvable_keys, maybe_resolvable_values)))
|
|
79
|
+
if is_keys_resolvable and is_values_resolvable:
|
|
80
|
+
return out.resolve(
|
|
81
|
+
resolved_variables=resolved_variables,
|
|
82
|
+
custom_functions=custom_functions,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return out
|
|
86
|
+
|
|
58
87
|
def future_resolvable_type(self) -> Type[Resolvable]:
|
|
59
88
|
return Map
|
|
60
89
|
|
|
@@ -3,6 +3,7 @@ from typing import Dict
|
|
|
3
3
|
from typing import List
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
|
+
from ytdl_sub.script.types.function import BuiltInFunction
|
|
6
7
|
from ytdl_sub.script.types.resolvable import Argument
|
|
7
8
|
from ytdl_sub.script.types.resolvable import Resolvable
|
|
8
9
|
from ytdl_sub.script.types.resolvable import String
|
|
@@ -15,7 +16,7 @@ class SyntaxTree(VariableDependency):
|
|
|
15
16
|
ast: List[Argument]
|
|
16
17
|
|
|
17
18
|
@property
|
|
18
|
-
def
|
|
19
|
+
def iterable_arguments(self) -> List[Argument]:
|
|
19
20
|
return self.ast
|
|
20
21
|
|
|
21
22
|
def resolve(
|
|
@@ -40,6 +41,26 @@ class SyntaxTree(VariableDependency):
|
|
|
40
41
|
# Otherwise, to concat multiple resolved outputs, we must concat as strings
|
|
41
42
|
return String("".join([str(res) for res in resolved]))
|
|
42
43
|
|
|
44
|
+
def partial_resolve(
|
|
45
|
+
self,
|
|
46
|
+
resolved_variables: Dict[Variable, Resolvable],
|
|
47
|
+
unresolved_variables: Dict[Variable, Argument],
|
|
48
|
+
custom_functions: Dict[str, VariableDependency],
|
|
49
|
+
) -> Argument | Resolvable:
|
|
50
|
+
# Ensure this does not get returned as a SyntaxTree since nesting them is not supported.
|
|
51
|
+
maybe_resolvable_values, _ = VariableDependency.try_partial_resolve(
|
|
52
|
+
args=self.ast,
|
|
53
|
+
resolved_variables=resolved_variables,
|
|
54
|
+
unresolved_variables=unresolved_variables,
|
|
55
|
+
custom_functions=custom_functions,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Mimic the above resolve behavior
|
|
59
|
+
if len(maybe_resolvable_values) > 1:
|
|
60
|
+
return BuiltInFunction(name="concat", args=maybe_resolvable_values)
|
|
61
|
+
|
|
62
|
+
return maybe_resolvable_values[0]
|
|
63
|
+
|
|
43
64
|
@property
|
|
44
65
|
def maybe_resolvable(self) -> Optional[Resolvable]:
|
|
45
66
|
"""
|
|
@@ -76,3 +97,11 @@ class ResolvedSyntaxTree(SyntaxTree):
|
|
|
76
97
|
@property
|
|
77
98
|
def maybe_resolvable(self) -> Optional[Resolvable]:
|
|
78
99
|
return self.ast[0]
|
|
100
|
+
|
|
101
|
+
def partial_resolve(
|
|
102
|
+
self,
|
|
103
|
+
resolved_variables: Dict[Variable, Resolvable],
|
|
104
|
+
unresolved_variables: Dict[Variable, Argument],
|
|
105
|
+
custom_functions: Dict[str, VariableDependency],
|
|
106
|
+
) -> Argument | Resolvable:
|
|
107
|
+
return self.ast[0]
|
|
@@ -5,6 +5,7 @@ from typing import Dict
|
|
|
5
5
|
from typing import Iterable
|
|
6
6
|
from typing import List
|
|
7
7
|
from typing import Set
|
|
8
|
+
from typing import Tuple
|
|
8
9
|
from typing import Type
|
|
9
10
|
from typing import TypeVar
|
|
10
11
|
from typing import final
|
|
@@ -27,7 +28,7 @@ TypeT = TypeVar("TypeT")
|
|
|
27
28
|
class VariableDependency(ABC):
|
|
28
29
|
@property
|
|
29
30
|
@abstractmethod
|
|
30
|
-
def
|
|
31
|
+
def iterable_arguments(self) -> List[Argument]:
|
|
31
32
|
"""
|
|
32
33
|
Returns
|
|
33
34
|
-------
|
|
@@ -38,7 +39,7 @@ class VariableDependency(ABC):
|
|
|
38
39
|
self, ttype: Type[TypeT], subclass: bool = False, instance: bool = True
|
|
39
40
|
) -> List[TypeT]:
|
|
40
41
|
output: List[TypeT] = []
|
|
41
|
-
for arg in self.
|
|
42
|
+
for arg in self.iterable_arguments:
|
|
42
43
|
if subclass and issubclass(type(arg), ttype):
|
|
43
44
|
output.append(arg)
|
|
44
45
|
elif instance and isinstance(arg, ttype):
|
|
@@ -104,7 +105,7 @@ class VariableDependency(ABC):
|
|
|
104
105
|
All CustomFunctions that this depends on.
|
|
105
106
|
"""
|
|
106
107
|
output: Set[ParsedCustomFunction] = set()
|
|
107
|
-
for arg in self.
|
|
108
|
+
for arg in self.iterable_arguments:
|
|
108
109
|
if isinstance(arg, NamedCustomFunction):
|
|
109
110
|
if not isinstance(arg, FunctionType):
|
|
110
111
|
# A NamedCustomFunction should also always be a FunctionType
|
|
@@ -138,6 +139,28 @@ class VariableDependency(ABC):
|
|
|
138
139
|
Resolved value
|
|
139
140
|
"""
|
|
140
141
|
|
|
142
|
+
@abstractmethod
|
|
143
|
+
def partial_resolve(
|
|
144
|
+
self,
|
|
145
|
+
resolved_variables: Dict[Variable, Resolvable],
|
|
146
|
+
unresolved_variables: Dict[Variable, Argument],
|
|
147
|
+
custom_functions: Dict[str, "VariableDependency"],
|
|
148
|
+
) -> Argument | Resolvable:
|
|
149
|
+
"""
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
resolved_variables
|
|
153
|
+
Lookup of variables that have been resolved
|
|
154
|
+
unresolved_variables
|
|
155
|
+
Lookup of variables that have not been resolved
|
|
156
|
+
custom_functions
|
|
157
|
+
Lookup of any custom functions that have been parsed
|
|
158
|
+
|
|
159
|
+
Returns
|
|
160
|
+
-------
|
|
161
|
+
Either a fully resolved value or partially resolved value of the same type.
|
|
162
|
+
"""
|
|
163
|
+
|
|
141
164
|
@classmethod
|
|
142
165
|
def _resolve_argument_type(
|
|
143
166
|
cls,
|
|
@@ -222,3 +245,43 @@ class VariableDependency(ABC):
|
|
|
222
245
|
):
|
|
223
246
|
return True
|
|
224
247
|
return len(self.variables.intersection(variables)) > 0
|
|
248
|
+
|
|
249
|
+
@classmethod
|
|
250
|
+
def try_partial_resolve(
|
|
251
|
+
cls,
|
|
252
|
+
args: Iterable[Argument],
|
|
253
|
+
resolved_variables: Dict[Variable, Resolvable],
|
|
254
|
+
unresolved_variables: Dict[Variable, Argument],
|
|
255
|
+
custom_functions: Dict[str, "VariableDependency"],
|
|
256
|
+
) -> Tuple[List[Argument], bool]:
|
|
257
|
+
"""
|
|
258
|
+
Attempts to resolve a list of arguments. Returns a tuple of them post partially resolved,
|
|
259
|
+
and a boolean indicating whether all of them are fully resolved.
|
|
260
|
+
"""
|
|
261
|
+
maybe_resolvable_args: List[Argument] = []
|
|
262
|
+
is_resolvable = True
|
|
263
|
+
for arg in args:
|
|
264
|
+
maybe_resolvable_args.append(arg)
|
|
265
|
+
|
|
266
|
+
if isinstance(arg, Lambda) and arg.value in custom_functions:
|
|
267
|
+
if not custom_functions[arg.value].is_subset_of(
|
|
268
|
+
variables=resolved_variables,
|
|
269
|
+
custom_function_definitions=custom_functions,
|
|
270
|
+
):
|
|
271
|
+
is_resolvable = False
|
|
272
|
+
elif isinstance(arg, VariableDependency):
|
|
273
|
+
maybe_resolvable_args[-1] = arg.partial_resolve(
|
|
274
|
+
resolved_variables=resolved_variables,
|
|
275
|
+
unresolved_variables=unresolved_variables,
|
|
276
|
+
custom_functions=custom_functions,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
if not isinstance(maybe_resolvable_args[-1], Resolvable):
|
|
280
|
+
is_resolvable = False
|
|
281
|
+
elif isinstance(arg, Variable):
|
|
282
|
+
if arg not in resolved_variables:
|
|
283
|
+
is_resolvable = False
|
|
284
|
+
if arg in unresolved_variables:
|
|
285
|
+
maybe_resolvable_args[-1] = unresolved_variables[arg]
|
|
286
|
+
|
|
287
|
+
return maybe_resolvable_args, is_resolvable
|