ytdl-sub 2026.1.11__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.11 → ytdl_sub-2026.1.13}/PKG-INFO +1 -1
- ytdl_sub-2026.1.13/src/ytdl_sub/__init__.py +1 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/script.py +59 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/array.py +22 -1
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/function.py +178 -1
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/map.py +30 -1
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/syntax_tree.py +30 -1
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/variable_dependency.py +66 -3
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/script.py +37 -2
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/PKG-INFO +1 -1
- ytdl_sub-2026.1.11/src/ytdl_sub/__init__.py +0 -1
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/LICENSE +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/README.md +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/pyproject.toml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/setup.cfg +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/entrypoint.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/output_summary.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/output_transaction_log.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/cli_to_sub.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/dl.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/main.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/config_file.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/config_validator.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/defaults.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/overrides.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/plugin.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/plugin_mapping.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/plugin_operation.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/preset_plugins.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/preset.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/preset_options.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/validators/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/validators/options.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/validators/variable_validation.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/info_json/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/info_json/info_json_downloader.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/source_plugin.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/url/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/url/downloader.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/url/validators.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/ytdl_options_builder.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/ytdlp.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/base_entry.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/entry.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/entry_parent.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/custom_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/function_scripts.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/variable_definitions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/variable_types.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/variables/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/variables/override_variables.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/main.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/audio_extract.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/chapters.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/date_range.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/embed_thumbnail.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/file_convert.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/filter_exclude.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/filter_include.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/format.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/internal/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/internal/view.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/match_filters.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/music_tags.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/nfo_tags.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/output_directory_nfo_tags.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/split_by_chapters.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/square_thumbnail.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/static_nfo_tags.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/subtitles.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/throttle_protection.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/video_tags.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/download_deletion_options.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/filter_duration.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/filter_keywords.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/media_quality.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/players.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/throttle_protection.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/url.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/url_bilateral.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/url_categorized.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/internal/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/internal/view.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/albums_from_chapters.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/albums_from_playlists.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/singles.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/soundcloud.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_base.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_extras.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/music_videos.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/episode.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_by_date.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_collection.yaml +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/array_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/boolean_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/conditional_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/date_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/error_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/json_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/map_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/numeric_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/print_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/regex_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/string_functions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/parser.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/script_output.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/resolvable.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/variable.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/exception_formatters.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/exceptions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/name_validation.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/type_checking.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/base_subscription.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription_download.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription_validators.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription_ytdl_options.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/thread/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/thread/log_entries_downloaded_listener.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/chapters.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/datetime.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/exceptions.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/ffmpeg.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/file_handler.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/file_lock.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/file_path.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/logger.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/retry.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/scriptable.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/subtitles.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/system.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/thumbnail.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/xml.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/yaml.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/audo_codec_validator.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/file_path_validators.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/nfo_validators.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/regex_validator.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/source_variable_validator.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/strict_dict_validator.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/string_datetime.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/string_formatter_validators.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/string_select_validator.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/validators.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/ytdl_additions/__init__.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub/ytdl_additions/enhanced_download_archive.py +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/SOURCES.txt +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/dependency_links.txt +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/entry_points.txt +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/requires.txt +0 -0
- {ytdl_sub-2026.1.11 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__pypi_version__ = "2026.01.13";__local_version__ = "2026.01.13+530d2f7"
|
|
@@ -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
|
|
@@ -2,11 +2,14 @@ import json
|
|
|
2
2
|
import re
|
|
3
3
|
from typing import Any
|
|
4
4
|
from typing import Dict
|
|
5
|
+
from typing import Optional
|
|
5
6
|
|
|
6
7
|
from ytdl_sub.script.parser import parse
|
|
8
|
+
from ytdl_sub.script.types.array import Array
|
|
7
9
|
from ytdl_sub.script.types.array import UnresolvedArray
|
|
8
10
|
from ytdl_sub.script.types.function import BuiltInFunction
|
|
9
11
|
from ytdl_sub.script.types.function import Function
|
|
12
|
+
from ytdl_sub.script.types.map import Map
|
|
10
13
|
from ytdl_sub.script.types.map import UnresolvedMap
|
|
11
14
|
from ytdl_sub.script.types.resolvable import Argument
|
|
12
15
|
from ytdl_sub.script.types.resolvable import Boolean
|
|
@@ -137,9 +140,9 @@ class ScriptUtils:
|
|
|
137
140
|
out = f"%bool({arg.native})"
|
|
138
141
|
elif isinstance(arg, Float):
|
|
139
142
|
out = f"%float({arg.native})"
|
|
140
|
-
elif isinstance(arg, UnresolvedArray):
|
|
143
|
+
elif isinstance(arg, (Array, UnresolvedArray)):
|
|
141
144
|
out = f"[ {', '.join(cls._to_script_code(val) for val in arg.value)} ]"
|
|
142
|
-
elif isinstance(arg, UnresolvedMap):
|
|
145
|
+
elif isinstance(arg, (Map, UnresolvedMap)):
|
|
143
146
|
kv_list = (
|
|
144
147
|
f"{cls._to_script_code(key)}: {cls._to_script_code(val)}"
|
|
145
148
|
for key, val in arg.value.items()
|
|
@@ -155,11 +158,43 @@ class ScriptUtils:
|
|
|
155
158
|
raise UNREACHABLE
|
|
156
159
|
return f"{{ {out} }}" if top_level else out
|
|
157
160
|
|
|
161
|
+
@classmethod
|
|
162
|
+
def _is_top_level_string(cls, tree: SyntaxTree) -> Optional[str]:
|
|
163
|
+
if not (
|
|
164
|
+
len(tree.ast) == 1
|
|
165
|
+
and isinstance(tree.ast[0], BuiltInFunction)
|
|
166
|
+
and tree.ast[0].name == "concat"
|
|
167
|
+
):
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
output = ""
|
|
171
|
+
for arg in tree.ast[0].args:
|
|
172
|
+
if isinstance(arg, BuiltInFunction) and arg.name == "string" and len(arg.args) == 1:
|
|
173
|
+
output += cls._to_script_code(arg.args[0], top_level=True)
|
|
174
|
+
else:
|
|
175
|
+
output += cls._to_script_code(arg, top_level=True)
|
|
176
|
+
|
|
177
|
+
return output
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def _syntax_tree_to_native_script(cls, tree: SyntaxTree) -> str:
|
|
181
|
+
|
|
182
|
+
if (output := cls._is_top_level_string(tree)) is not None:
|
|
183
|
+
return output
|
|
184
|
+
|
|
185
|
+
output = ""
|
|
186
|
+
for arg in tree.ast:
|
|
187
|
+
output += cls._to_script_code(arg, top_level=True)
|
|
188
|
+
return output
|
|
189
|
+
|
|
158
190
|
@classmethod
|
|
159
191
|
def to_native_script(cls, value: Any) -> str:
|
|
160
192
|
"""
|
|
161
193
|
Converts any JSON-compatible value into equivalent script syntax
|
|
162
194
|
"""
|
|
195
|
+
if isinstance(value, SyntaxTree):
|
|
196
|
+
return cls._syntax_tree_to_native_script(value)
|
|
197
|
+
|
|
163
198
|
return cls._to_script_code(cls._to_script_argument(value), top_level=True)
|
|
164
199
|
|
|
165
200
|
@classmethod
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__pypi_version__ = "2026.01.11";__local_version__ = "2026.01.11+41a5bf6"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|