diff --git a/README-ytdlp-ops-auth.md b/README-ytdlp-ops-auth.md new file mode 100644 index 0000000..06ffb16 --- /dev/null +++ b/README-ytdlp-ops-auth.md @@ -0,0 +1,97 @@ +# YTDLP Client Side Integration + +This document describes how to integrate and use the YTDLP client with the token service. + +## Build + +1. **Pull, configure and start server if needed:** + ```bash + cd /srv/airflow_worker/ + docker login pangramia # It used to be performed beforehand otherwise ask pull password + docker compose -f docker-compose-ytdlp-ops.yaml up -d + docker compose -f docker-compose-ytdlp-ops.yaml logs -f + ``` + The server is bound to a certain proxy, like "socks5://sslocal-rust-1084:1084". + + Also check that redis in bind to 0.0.0.0 in config + +2. **Build airflow-worker with custom dependencies:** + ```bash + cd /srv/airflow_worker/ + docker compose build airflow-worker + docker compose down airflow-worker + docker compose up -d --no-deps airflow-worker + ``` + +3. **Test the built-in client:** + ```bash + # Show client help + docker compose exec airflow-worker python /app/ytdlp_ops_client.py --help + + # Get token and info.json + docker compose exec airflow-worker python /app/ytdlp_ops_client.py --host 85.192.30.55 --port 9090 getToken --url 'https://www.youtube.com/watch?v=vKTVLpmvznI' + + # List formats using saved info.json + docker compose exec airflow-worker yt-dlp --load-info-json "latest.json" -F + + # Simulate download using saved info.json + docker compose exec airflow-worker yt-dlp --load-info-json "latest.json" --proxy "socks5://sslocal-rust-1084:1084" --simulate --verbose + + # Extract metadata and download URLs using jq + docker compose exec airflow-worker jq -r '"Title: \(.title)", "Date: \(.upload_date | strptime("%Y%m%d") | strftime("%Y-%m-%d"))", "Author: \(.uploader)", "Length: \(.duration_string)", "", "Download URLs:", (.formats[] | select(.vcodec != "none" or .acodec != "none") | .url)' latest.json + ``` + +4. **Test Airflow task:** + + To run the `ytdlp_client_dag_v2.1` DAG: + + Set up required Airflow variables + ```bash + docker compose exec airflow-worker airflow variables set DOWNLOAD_OPTIONS '{"formats": ["bestvideo[height<=1080]+bestaudio/best[height<=1080]"]}' + docker compose exec airflow-worker airflow variables set DOWNLOADS_TEMP '/opt/airflow/downloadfiles' + docker compose exec airflow-worker airflow variables set DOWNLOADS_PATH '/opt/airflow/downloadfiles' + + docker compose exec airflow-worker airflow variables list + docker compose exec airflow-worker airflow variables set TOKEN_TIMEOUT '300' + + docker compose exec airflow-worker airflow connections import /opt/airflow/config/docker_hub_repo.json + docker compose exec airflow-worker airflow connections delete redis_default + docker compose exec airflow-worker airflow connections import /opt/airflow/config/redis_default_conn.json + ``` + + + **Using direct connection with task test:** + ```bash + docker compose exec airflow-worker airflow db reset + docker compose exec airflow-worker airflow dags reserialize + + docker compose exec airflow-worker airflow dags list + docker compose exec airflow-worker airflow dags list-import-errors + docker compose exec airflow-worker airflow tasks test ytdlp_client_dag_v2.1 get_token $(date -u +"%Y-%m-%dT%H:%M:%S+00:00") --task-params '{"url": "https://www.youtube.com/watch?v=sOlTX9uxUtM", "redis_enabled": false, "service_ip": "85.192.30.55", "service_port": 9090}' + docker compose exec airflow-worker yt-dlp --load-info-json /opt/airflow/downloadfiles/latest.json --proxy "socks5://sslocal-rust-1084:1084" --verbose --simulate + + docker compose exec airflow-worker airflow dags list-runs -d ytdlp_client_dag + + + + + + ``` + + + or deploy using trigger + ```bash + docker compose exec airflow-worker airflow dags list + docker compose exec airflow-worker airflow dags unpause ytdlp_client_dag_v2.1 + + // Try UI or recheck if works from server deploy + docker compose exec airflow-worker airflow dags trigger ytdlp_client_dag_v2.1 -c '{"url": "https://www.youtube.com/watch?v=sOlTX9uxUtM", "redis_enabled": false, "service_ip": "85.192.30.55", "service_port": 9090}' + + ``` + + + Check Redis for stored data by videoID + ```bash + docker compose exec redis redis-cli -a XXXXXX -h 89.253.221.173 -p 52909 HGETALL "token_info:sOlTX9uxUtM" | jq -R -s 'split("\n") | del(.[] | select(. == "")) | [.[range(0;length;2)]]' + ``` + diff --git a/README.md b/README.md index 06ffb16..8afe76f 100644 --- a/README.md +++ b/README.md @@ -1,97 +1,100 @@ -# YTDLP Client Side Integration +# YTDLP Airflow DAGs -This document describes how to integrate and use the YTDLP client with the token service. +This document describes the Airflow DAGs used for interacting with the YTDLP Ops service and managing processing queues. -## Build +## DAG Descriptions -1. **Pull, configure and start server if needed:** - ```bash - cd /srv/airflow_worker/ - docker login pangramia # It used to be performed beforehand otherwise ask pull password - docker compose -f docker-compose-ytdlp-ops.yaml up -d - docker compose -f docker-compose-ytdlp-ops.yaml logs -f - ``` - The server is bound to a certain proxy, like "socks5://sslocal-rust-1084:1084". +### `ytdlp_client_dag_v2.1` - Also check that redis in bind to 0.0.0.0 in config +* **File:** `airflow/dags/ytdlp_client_dag_v2.1.py` +* **Purpose:** Provides a way to test the YTDLP Ops Thrift service interaction for a *single* video URL. Useful for debugging connection issues, testing specific account IDs, or verifying the service response for a particular URL independently of the queueing system. +* **Parameters (Defaults):** + * `url` (`'https://www.youtube.com/watch?v=sOlTX9uxUtM'`): The video URL to process. + * `redis_enabled` (`False`): Use Redis for service discovery? + * `service_ip` (`'85.192.30.55'`): Service IP if `redis_enabled=False`. + * `service_port` (`9090`): Service port if `redis_enabled=False`. + * `account_id` (`'account_fr_2025-04-03T1220_anonomyous_2ssdfsf2342afga09'`): Account ID for lookup or call. + * `timeout` (`30`): Timeout in seconds for Thrift connection. + * `info_json_dir` (`"{{ var.value.get('DOWNLOADS_TEMP', '/opt/airflow/downloadfiles') }}"`): Directory to save `info.json`. +* **Results:** + * Connects to the YTDLP Ops service using the specified method (Redis or direct IP). + * Retrieves token data for the given URL and account ID. + * Saves the video's `info.json` metadata to the specified directory. + * Extracts the SOCKS proxy (if available). + * Pushes `info_json_path`, `socks_proxy`, and the original `ytdlp_command` (with tokens) to XCom. + * Optionally stores detailed results in a Redis hash (`token_info:`). -2. **Build airflow-worker with custom dependencies:** - ```bash - cd /srv/airflow_worker/ - docker compose build airflow-worker - docker compose down airflow-worker - docker compose up -d --no-deps airflow-worker - ``` +### `ytdlp_mgmt_queue_add_urls` -3. **Test the built-in client:** - ```bash - # Show client help - docker compose exec airflow-worker python /app/ytdlp_ops_client.py --help +* **File:** `airflow/dags/ytdlp_mgmt_queue_add_urls.py` +* **Purpose:** Manually add video URLs to a specific YTDLP inbox queue (Redis List). +* **Parameters (Defaults):** + * `redis_conn_id` (`'redis_default'`): Airflow Redis connection ID. + * `queue_name` (`'video_queue_inbox_account_fr_2025-04-03T1220_anonomyous_2ssdfsf2342afga09'`): Target Redis list (inbox queue). + * `urls` (`""`): Multiline string of video URLs to add. +* **Results:** + * Parses the multiline `urls` parameter. + * Adds each valid URL to the end of the Redis list specified by `queue_name`. + * Logs the number of URLs added. - # Get token and info.json - docker compose exec airflow-worker python /app/ytdlp_ops_client.py --host 85.192.30.55 --port 9090 getToken --url 'https://www.youtube.com/watch?v=vKTVLpmvznI' +### `ytdlp_mgmt_queue_clear` - # List formats using saved info.json - docker compose exec airflow-worker yt-dlp --load-info-json "latest.json" -F +* **File:** `airflow/dags/ytdlp_mgmt_queue_clear.py` +* **Purpose:** Manually delete a specific Redis key used by the YTDLP queues. +* **Parameters (Defaults):** + * `redis_conn_id` (`'redis_default'`): Airflow Redis connection ID. + * `queue_to_clear` (`'PLEASE_SPECIFY_QUEUE_TO_CLEAR'`): Exact name of the Redis key to clear. **Must be changed by user.** +* **Results:** + * Deletes the Redis key specified by the `queue_to_clear` parameter. + * **Warning:** This operation is destructive and irreversible. Use with extreme caution. Ensure you specify the correct key name (e.g., `video_queue_inbox_account_xyz`, `video_queue_progress`, `video_queue_result`, `video_queue_fail`). - # Simulate download using saved info.json - docker compose exec airflow-worker yt-dlp --load-info-json "latest.json" --proxy "socks5://sslocal-rust-1084:1084" --simulate --verbose +### `ytdlp_mgmt_queue_check_status` - # Extract metadata and download URLs using jq - docker compose exec airflow-worker jq -r '"Title: \(.title)", "Date: \(.upload_date | strptime("%Y%m%d") | strftime("%Y-%m-%d"))", "Author: \(.uploader)", "Length: \(.duration_string)", "", "Download URLs:", (.formats[] | select(.vcodec != "none" or .acodec != "none") | .url)' latest.json - ``` +* **File:** `airflow/dags/ytdlp_mgmt_queue_check_status.py` +* **Purpose:** Manually check the type and size of a specific YTDLP Redis queue/key. +* **Parameters (Defaults):** + * `redis_conn_id` (`'redis_default'`): Airflow Redis connection ID. + * `queue_to_check` (`'video_queue_inbox_account_fr_2025-04-03T1220_anonomyous_2ssdfsf2342afga09'`): Exact name of the Redis key to check. +* **Results:** + * Connects to Redis and determines the type of the key specified by `queue_to_check`. + * Determines the size (length for lists, number of fields for hashes). + * Logs the key type and size. + * Pushes `queue_key_type` and `queue_size` to XCom. -4. **Test Airflow task:** +### `ytdlp_mgmt_queue_list_contents` - To run the `ytdlp_client_dag_v2.1` DAG: +* **File:** `airflow/dags/ytdlp_mgmt_queue_list_contents.py` +* **Purpose:** Manually list the contents of a specific YTDLP Redis queue/key (list or hash). Useful for inspecting queue state or results. +* **Parameters (Defaults):** + * `redis_conn_id` (`'redis_default'`): Airflow Redis connection ID. + * `queue_to_list` (`'video_queue_inbox_account_fr_2025-04-03T1220_anonomyous_2ssdfsf2342afga09'`): Exact name of the Redis key to list. + * `max_items` (`100`): Maximum number of items/fields to list. +* **Results:** + * Connects to Redis and identifies the type of the key specified by `queue_to_list`. + * If it's a List, logs the first `max_items` elements. + * If it's a Hash, logs up to `max_items` key-value pairs, attempting to pretty-print JSON values. + * Logs warnings for very large hashes. - Set up required Airflow variables - ```bash - docker compose exec airflow-worker airflow variables set DOWNLOAD_OPTIONS '{"formats": ["bestvideo[height<=1080]+bestaudio/best[height<=1080]"]}' - docker compose exec airflow-worker airflow variables set DOWNLOADS_TEMP '/opt/airflow/downloadfiles' - docker compose exec airflow-worker airflow variables set DOWNLOADS_PATH '/opt/airflow/downloadfiles' - - docker compose exec airflow-worker airflow variables list - docker compose exec airflow-worker airflow variables set TOKEN_TIMEOUT '300' +### `ytdlp_proc_sequential_processor` - docker compose exec airflow-worker airflow connections import /opt/airflow/config/docker_hub_repo.json - docker compose exec airflow-worker airflow connections delete redis_default - docker compose exec airflow-worker airflow connections import /opt/airflow/config/redis_default_conn.json - ``` - - - **Using direct connection with task test:** - ```bash - docker compose exec airflow-worker airflow db reset - docker compose exec airflow-worker airflow dags reserialize - - docker compose exec airflow-worker airflow dags list - docker compose exec airflow-worker airflow dags list-import-errors - docker compose exec airflow-worker airflow tasks test ytdlp_client_dag_v2.1 get_token $(date -u +"%Y-%m-%dT%H:%M:%S+00:00") --task-params '{"url": "https://www.youtube.com/watch?v=sOlTX9uxUtM", "redis_enabled": false, "service_ip": "85.192.30.55", "service_port": 9090}' - docker compose exec airflow-worker yt-dlp --load-info-json /opt/airflow/downloadfiles/latest.json --proxy "socks5://sslocal-rust-1084:1084" --verbose --simulate - - docker compose exec airflow-worker airflow dags list-runs -d ytdlp_client_dag - - - - - - ``` - - - or deploy using trigger - ```bash - docker compose exec airflow-worker airflow dags list - docker compose exec airflow-worker airflow dags unpause ytdlp_client_dag_v2.1 - - // Try UI or recheck if works from server deploy - docker compose exec airflow-worker airflow dags trigger ytdlp_client_dag_v2.1 -c '{"url": "https://www.youtube.com/watch?v=sOlTX9uxUtM", "redis_enabled": false, "service_ip": "85.192.30.55", "service_port": 9090}' - - ``` - - - Check Redis for stored data by videoID - ```bash - docker compose exec redis redis-cli -a XXXXXX -h 89.253.221.173 -p 52909 HGETALL "token_info:sOlTX9uxUtM" | jq -R -s 'split("\n") | del(.[] | select(. == "")) | [.[range(0;length;2)]]' - ``` - +* **File:** `airflow/dags/ytdlp_proc_sequential_processor.py` +* **Purpose:** Processes YouTube URLs sequentially from a Redis queue. Designed for batch processing. Pops a URL, gets token/metadata via YTDLP Ops service, downloads the media using `yt-dlp`, and records the result. +* **Parameters (Defaults):** + * `queue_name` (`'video_queue'`): Base name for Redis queues (e.g., `video_queue_inbox`, `video_queue_progress`). + * `redis_conn_id` (`'redis_default'`): Airflow Redis connection ID. + * `redis_enabled` (`False`): Use Redis for service discovery? If False, uses `service_ip`/`port`. + * `service_ip` (`None`): Required Service IP if `redis_enabled=False`. + * `service_port` (`None`): Required Service port if `redis_enabled=False`. + * `account_id` (`'default_account'`): Account ID for the API call (used for Redis lookup if `redis_enabled=True`). + * `timeout` (`30`): Timeout in seconds for the Thrift connection. + * `download_format` (`'ba[ext=m4a]/bestaudio/best'`): yt-dlp format selection string. + * `output_path_template` (`"{{ var.value.get('DOWNLOADS_TEMP', '/opt/airflow/downloads') }}/%(title)s [%(id)s].%(ext)s"`): yt-dlp output template. Uses Airflow Variable `DOWNLOADS_TEMP`. + * `info_json_dir` (`"{{ var.value.get('DOWNLOADS_TEMP', '/opt/airflow/downloadfiles') }}"`): Directory to save `info.json`. Uses Airflow Variable `DOWNLOADS_TEMP`. +* **Results:** + * Pops one URL from the `{{ params.queue_name }}_inbox` Redis list. + * If a URL is popped, it's added to the `{{ params.queue_name }}_progress` Redis hash. + * The `YtdlpOpsOperator` (`get_token` task) attempts to get token data (including `info.json`, proxy, command) for the URL using the specified connection method and account ID. + * If token retrieval succeeds, the `download_video` task executes `yt-dlp` using the retrieved `info.json`, proxy, the `download_format` parameter, and the `output_path_template` parameter to download the actual media. + * **On Successful Download:** The URL is removed from the progress hash and added to the `{{ params.queue_name }}_result` hash along with results (`info_json_path`, `socks_proxy`, `ytdlp_command`). + * **On Failure (Token Retrieval or Download):** The URL is removed from the progress hash and added to the `{{ params.queue_name }}_fail` hash along with error details (message, traceback). + * If the inbox queue is empty, the DAG run skips processing via `AirflowSkipException`. diff --git a/camoufox/Dockerfile b/camoufox/Dockerfile new file mode 100644 index 0000000..5e48661 --- /dev/null +++ b/camoufox/Dockerfile @@ -0,0 +1,42 @@ +# Use a base Python image +FROM python:3.11-slim + +# Set working directory +WORKDIR /app + +# Install necessary system packages for Playwright, GeoIP, and Xvfb +RUN apt-get update && apt-get install -y --no-install-recommends \ + libgeoip1 \ + # Xvfb for headless browser display + xvfb \ + # Playwright browser dependencies + libnss3 libnspr4 libdbus-1-3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2 \ + && rm -rf /var/lib/apt/lists/* + +# Install Python dependencies: camoufox with geoip support and playwright==1.49 +# Using --no-cache-dir to reduce image size +RUN pip install --no-cache-dir "camoufox[geoip]" playwright==1.49 + +# Install Playwright browsers for version 1.49 +RUN playwright install --with-deps + +# Copy the server script into the image +COPY camoufox_server.py . + +# Create directory for extensions and copy them +RUN mkdir /app/extensions +COPY google_sign_in_popup_blocker-1.0.2.xpi /app/extensions/ +COPY spoof_timezone-0.3.4.xpi /app/extensions/ +COPY youtube_ad_auto_skipper-0.6.0.xpi /app/extensions/ + +# Expose the default port Camoufox might use (adjust if needed) +# This is informational; the actual port mapping is in docker-compose. +EXPOSE 12345 + +# Copy the wrapper script and make it executable +COPY start_camoufox.sh /app/ +RUN chmod +x /app/start_camoufox.sh + +# Default command executes the wrapper script. +# Arguments for camoufox_server.py will be passed via docker-compose command section. +ENTRYPOINT ["/app/start_camoufox.sh"] diff --git a/camoufox/camoufox_server.py b/camoufox/camoufox_server.py new file mode 100644 index 0000000..fe24115 --- /dev/null +++ b/camoufox/camoufox_server.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 +import re +import argparse +import atexit # Import atexit +import shutil # Import shutil for directory removal +import logging # Import the logging module +import sys # Import sys for stdout +import os # Import os module +from camoufox.server import launch_server + +def parse_proxy_url(url): + """Parse proxy URL in format proto://user:pass@host:port""" + pattern = r'([^:]+)://(?:([^:]+):([^@]+)@)?([^:]+):(\d+)' + match = re.match(pattern, url) + if not match: + raise ValueError('Invalid proxy URL format. Expected proto://[user:pass@]host:port') + + proto, username, password, host, port = match.groups() + + # Ensure username and password are strings, not None + proxy_config = { + 'server': f'{proto}://{host}:{port}', + 'username': username or '', + 'password': password or '' + } + + # Remove empty credentials + if not proxy_config['username']: + del proxy_config['username'] + if not proxy_config['password']: + del proxy_config['password'] + + return proxy_config + +def main(): + parser = argparse.ArgumentParser(description='Launch Camoufox server with optional proxy support') + parser.add_argument('--proxy-url', help='Optional proxy URL in format proto://user:pass@host:port (supports http, https, socks5)') + parser.add_argument('--ws-host', default='localhost', help='WebSocket server host address (e.g., localhost, 0.0.0.0)') + parser.add_argument('--port', type=int, default=0, help='WebSocket server port (0 for random)') + parser.add_argument('--ws-path', default='camoufox', help='WebSocket server path') + parser.add_argument('--headless', action='store_true', help='Run browser in headless mode') + parser.add_argument('--geoip', nargs='?', const=True, default=False, + help='Enable geo IP protection. Can specify IP address or use True for automatic detection') + parser.add_argument('--locale', help='Locale(s) to use (e.g. "en-US" or "en-US,fr-FR")') + parser.add_argument('--block-images', action='store_true', help='Block image requests to save bandwidth') + parser.add_argument('--block-webrtc', action='store_true', help='Block WebRTC entirely') + parser.add_argument('--humanize', nargs='?', const=True, type=float, + help='Humanize cursor movements. Can specify max duration in seconds') + parser.add_argument('--extensions', type=str, + help='Comma-separated list of extension paths to enable (XPI files or extracted directories). Use quotes if paths contain spaces.') + + args = parser.parse_args() + + proxy_config = None + if args.proxy_url: + try: + proxy_config = parse_proxy_url(args.proxy_url) + print(f"Using proxy configuration: {args.proxy_url}") + except ValueError as e: + print(f'Error parsing proxy URL: {e}') + return + else: + print("No proxy URL provided. Running without proxy.") + + # --- Basic Logging Configuration --- + # Configure the root logger to show INFO level messages + # This might capture logs from camoufox or its dependencies (like websockets) + log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + log_handler = logging.StreamHandler(sys.stdout) # Log to standard output + log_handler.setFormatter(log_formatter) + + root_logger = logging.getLogger() + # Remove existing handlers to avoid duplicates if script is re-run in same process + for handler in root_logger.handlers[:]: + root_logger.removeHandler(handler) + root_logger.addHandler(log_handler) + # Set level to DEBUG for more detailed output from Camoufox/Playwright + root_logger.setLevel(logging.DEBUG) + + logging.debug("DEBUG logging enabled. Starting Camoufox server setup...") + # --- End Logging Configuration --- + + try: + # --- Check DISPLAY environment variable --- + display_var = os.environ.get('DISPLAY') + logging.info(f"Value of DISPLAY environment variable: {display_var}") + # --- End Check --- + + # Build config dictionary + config = { + 'headless': args.headless, + 'geoip': args.geoip, + # 'proxy': proxy_config, # Add proxy config only if it exists + 'host': args.ws_host, # Add the host argument + 'port': args.port, + 'ws_path': args.ws_path, + # Explicitly pass DISPLAY environment variable to Playwright + 'env': {'DISPLAY': os.environ.get('DISPLAY')} + } + # Add proxy to config only if it was successfully parsed + if proxy_config: + config['proxy'] = proxy_config + + # Add optional parameters + if args.locale: + config['locale'] = args.locale + if args.block_images: + config['block_images'] = True + if args.block_webrtc: + config['block_webrtc'] = True + if args.humanize: + config['humanize'] = args.humanize if isinstance(args.humanize, float) else True + + # Exclude default addons including uBlock Origin + config['exclude_addons'] = ['ublock_origin', 'default_addons'] + print('Excluded default addons including uBlock Origin') + + # Add custom extensions if specified + if args.extensions: + from pathlib import Path + valid_extensions = [] + + # Split comma-separated extensions + extensions_list = [ext.strip() for ext in args.extensions.split(',')] + temp_dirs_to_cleanup = [] # List to store temp dirs + + # Register cleanup function + def cleanup_temp_dirs(): + for temp_dir in temp_dirs_to_cleanup: + try: + shutil.rmtree(temp_dir) + print(f"Cleaned up temporary extension directory: {temp_dir}") + except Exception as e: + print(f"Warning: Failed to clean up temp dir {temp_dir}: {e}") + atexit.register(cleanup_temp_dirs) + + for ext_path in extensions_list: + # Convert to absolute path + ext_path = Path(ext_path).absolute() + + if not ext_path.exists(): + print(f"Warning: Extension path does not exist: {ext_path}") + continue + + if ext_path.is_file() and ext_path.suffix == '.xpi': + # Extract XPI to temporary directory + import tempfile + import zipfile + + try: + temp_dir = tempfile.mkdtemp(prefix=f"camoufox_ext_{ext_path.stem}_") + temp_dirs_to_cleanup.append(temp_dir) # Add to cleanup list + with zipfile.ZipFile(ext_path, 'r') as zip_ref: + zip_ref.extractall(temp_dir) + valid_extensions.append(temp_dir) + print(f"Successfully loaded extension: {ext_path.name} (extracted to {temp_dir})") + except Exception as e: + print(f"Error loading extension {ext_path}: {str(e)}") + # Remove from cleanup list if extraction failed before adding to valid_extensions + if temp_dir in temp_dirs_to_cleanup: + temp_dirs_to_cleanup.remove(temp_dir) + continue + elif ext_path.is_dir(): + # Check if it's a valid Firefox extension + if (ext_path / 'manifest.json').exists(): + valid_extensions.append(str(ext_path)) + print(f"Successfully loaded extension: {ext_path.name}") + else: + print(f"Warning: Directory is not a valid Firefox extension: {ext_path}") + else: + print(f"Warning: Invalid extension path: {ext_path}") + + if valid_extensions: + config['addons'] = valid_extensions + print(f"Loaded {len(valid_extensions)} extensions") + else: + print("Warning: No valid extensions were loaded") + + server = launch_server(**config) + except Exception as e: + print(f'Error launching server: {str(e)}') + if 'Browser.setBrowserProxy' in str(e): + print('Note: The browser may not support SOCKS5 proxy authentication') + return + + print(f'\nCamoufox server started successfully!') + print(f'WebSocket endpoint: {server.ws_endpoint}\n') + +if __name__ == '__main__': + main() diff --git a/camoufox/google_sign_in_popup_blocker-1.0.2.xpi b/camoufox/google_sign_in_popup_blocker-1.0.2.xpi new file mode 100644 index 0000000..8f12e5c Binary files /dev/null and b/camoufox/google_sign_in_popup_blocker-1.0.2.xpi differ diff --git a/camoufox/spoof_timezone-0.3.4.xpi b/camoufox/spoof_timezone-0.3.4.xpi new file mode 100644 index 0000000..26effcb Binary files /dev/null and b/camoufox/spoof_timezone-0.3.4.xpi differ diff --git a/camoufox/start_camoufox.sh b/camoufox/start_camoufox.sh new file mode 100755 index 0000000..0c69a2d --- /dev/null +++ b/camoufox/start_camoufox.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# Set error handling +set -e + +# Function to cleanup resources on exit +cleanup() { + echo "Cleaning up resources..." + + # Kill Xvfb if it's running + if [ -n "$XVFB_PID" ] && ps -p $XVFB_PID > /dev/null; then + echo "Stopping Xvfb (PID: $XVFB_PID)" + kill $XVFB_PID || true + fi + + # Remove X lock files if they exist + if [ -e "/tmp/.X99-lock" ]; then + echo "Removing X lock file" + rm -f /tmp/.X99-lock + fi + + echo "Cleanup complete" +} + +# Register the cleanup function to run on script exit +trap cleanup EXIT + +# Check if X lock file exists and remove it (in case of previous unclean shutdown) +if [ -e "/tmp/.X99-lock" ]; then + echo "Removing existing X lock file" + rm -f /tmp/.X99-lock +fi + +# Start Xvfb with display :99 +echo "Starting Xvfb on display :99" +Xvfb :99 -screen 0 1280x1024x24 -ac & +XVFB_PID=$! + +# Wait a moment for Xvfb to initialize +sleep 2 + +# Check if Xvfb started successfully +if ! ps -p $XVFB_PID > /dev/null; then + echo "Failed to start Xvfb" + exit 1 +fi + +# Export the DISPLAY variable for the browser +export DISPLAY=:99 + +echo "Xvfb started successfully with PID: $XVFB_PID" +echo "DISPLAY set to: $DISPLAY" + +# Start the Camoufox server with all arguments passed to this script +echo "Starting Camoufox server with arguments:" +printf " Arg: '%s'\n" "$@" # Print each argument quoted on a new line +echo "Executing: python3 camoufox_server.py $@" +python3 camoufox_server.py "$@" \ No newline at end of file diff --git a/camoufox/youtube_ad_auto_skipper-0.6.0.xpi b/camoufox/youtube_ad_auto_skipper-0.6.0.xpi new file mode 100644 index 0000000..51173f0 Binary files /dev/null and b/camoufox/youtube_ad_auto_skipper-0.6.0.xpi differ diff --git a/dags/ytdlp_client_dag_v2.1.py b/dags/ytdlp_client_dag_v2.1.py index 9782a59..34dcc5c 100644 --- a/dags/ytdlp_client_dag_v2.1.py +++ b/dags/ytdlp_client_dag_v2.1.py @@ -468,9 +468,10 @@ class YtdlpOpsOperator(BaseOperator): # Write to timestamped file try: + logger.info(f"Writing info.json content (received from service) to {info_json_path}...") with open(info_json_path, 'w', encoding='utf-8') as f: f.write(info_json) - logger.info(f"Saved info.json to timestamped file: {info_json_path}") + logger.info(f"Successfully saved info.json to timestamped file: {info_json_path}") except IOError as e: logger.error(f"Failed to write info.json to {info_json_path}: {e}") return None # Indicate failure diff --git a/dags/ytdlp_mgmt_queue_add_urls.py b/dags/ytdlp_mgmt_queue_add_urls.py new file mode 100644 index 0000000..71d135f --- /dev/null +++ b/dags/ytdlp_mgmt_queue_add_urls.py @@ -0,0 +1,189 @@ +from airflow import DAG +from airflow.models.param import Param +from airflow.operators.python import PythonOperator +from airflow.providers.redis.hooks.redis import RedisHook +from airflow.utils.dates import days_ago +from airflow.exceptions import AirflowException +from datetime import timedelta +import logging +import redis # Import redis exceptions if needed + +# Configure logging +logger = logging.getLogger(__name__) + +# Default settings +DEFAULT_QUEUE_NAME = 'video_queue_inbox' # Default to the inbox queue +DEFAULT_REDIS_CONN_ID = 'redis_default' + +# --- Helper Functions --- + +def _get_redis_client(redis_conn_id): + """Gets a Redis client connection using RedisHook.""" + try: + hook = RedisHook(redis_conn_id=redis_conn_id) + client = hook.get_conn() + client.ping() + logger.info(f"Successfully connected to Redis using connection '{redis_conn_id}'.") + return client + except redis.exceptions.AuthenticationError: + logger.error(f"Redis authentication failed for connection '{redis_conn_id}'. Check password.") + raise AirflowException(f"Redis authentication failed for '{redis_conn_id}'.") + except Exception as e: + logger.error(f"Failed to get Redis client for connection '{redis_conn_id}': {e}") + raise AirflowException(f"Redis connection failed for '{redis_conn_id}': {e}") + +# --- Python Callables for Tasks --- + +def add_urls_callable(**context): + """Adds URLs from comma/newline separated input to the specified Redis list.""" + params = context['params'] + redis_conn_id = params['redis_conn_id'] + queue_name = params['queue_name'] # Should be the inbox queue, e.g., video_queue_inbox + urls_input = params['urls'] + + if not queue_name.endswith('_inbox'): + logger.warning(f"Target queue name '{queue_name}' does not end with '_inbox'. Ensure this is the intended inbox queue.") + + if not urls_input or not isinstance(urls_input, str): + logger.warning("No URLs provided or 'urls' parameter is not a string. Nothing to add.") + return + + # Process input: split by newline, then by comma, flatten, strip, and filter empty + urls_to_add = [] + for line in urls_input.splitlines(): + urls_to_add.extend(url.strip() for url in line.split(',') if url.strip()) + + # Remove duplicates while preserving order (optional, but good practice) + seen = set() + urls_to_add = [x for x in urls_to_add if not (x in seen or seen.add(x))] + + if not urls_to_add: + logger.info("No valid URLs found after processing input. Nothing added.") + return + + logger.info(f"Attempting to add {len(urls_to_add)} unique URLs to Redis list '{queue_name}' using connection '{redis_conn_id}'.") + try: + redis_client = _get_redis_client(redis_conn_id) + # Use rpush to add to the end of the list (FIFO behavior with lpop) + added_count = redis_client.rpush(queue_name, *urls_to_add) + logger.info(f"Successfully added {len(urls_to_add)} URLs to list '{queue_name}'. New list length: {added_count}.") + except Exception as e: + logger.error(f"Failed to add URLs to Redis list '{queue_name}': {e}", exc_info=True) + raise AirflowException(f"Failed to add URLs to Redis: {e}") + + +# Removed clear_queue_callable as this DAG focuses on adding and verifying + + +def check_status_callable(**context): + """Checks the type and length/size of the specified Redis key.""" + # Access DAG run parameters directly from context['params'] + dag_params = context['params'] + redis_conn_id = dag_params['redis_conn_id'] + # Check the status of the queue specified in the main DAG parameters + queue_to_check = dag_params['queue_name'] + + if not queue_to_check: + raise ValueError("DAG parameter 'queue_name' cannot be empty.") + + logger.info(f"Attempting to check status of Redis key '{queue_to_check}' using connection '{redis_conn_id}'.") # Uses DAG param value + try: + # Use the resolved redis_conn_id to get the client + redis_client = _get_redis_client(redis_conn_id) + # redis_client.type returns bytes (e.g., b'list', b'hash', b'none') + key_type_bytes = redis_client.type(queue_to_check) + key_type_str = key_type_bytes.decode('utf-8') # Decode to string + + length = 0 + if key_type_str == 'list': + length = redis_client.llen(queue_to_check) + logger.info(f"Redis list '{queue_to_check}' has {length} items.") + elif key_type_str == 'hash': + length = redis_client.hlen(queue_to_check) + logger.info(f"Redis hash '{queue_to_check}' has {length} fields.") + elif key_type_str == 'none': # Check against the decoded string 'none' + logger.info(f"Redis key '{queue_to_check}' does not exist.") + else: + # Attempt to get size for other types if possible, e.g., set size + try: + if key_type_str == 'set': + length = redis_client.scard(queue_to_check) + logger.info(f"Redis set '{queue_to_check}' has {length} members.") + # Add checks for other types like zset if needed + else: + logger.info(f"Redis key '{queue_to_check}' exists but is of unhandled type '{key_type_str}'. Cannot determine size.") + except Exception as size_error: + logger.warning(f"Could not determine size for Redis key '{queue_to_check}' (type: {key_type_str}): {size_error}") + logger.info(f"Redis key '{queue_to_check}' exists but is of unhandled/unsizeable type '{key_type_str}'.") + + # Push results to XCom + context['task_instance'].xcom_push(key='queue_key_type', value=key_type_str) + context['task_instance'].xcom_push(key='queue_size', value=length) + # Return status info using the resolved queue_to_check + return {'key': queue_to_check, 'type': key_type_str, 'size': length} + + except Exception as e: + # Log error using the resolved queue_to_check + logger.error(f"Failed to check status of Redis key '{queue_to_check}': {e}", exc_info=True) + raise AirflowException(f"Failed to check Redis key status: {e}") + + +# --- DAG Definition --- +default_args = { + 'owner': 'airflow', + 'depends_on_past': False, + 'email_on_failure': False, + 'email_on_retry': False, + 'retries': 1, + 'retry_delay': timedelta(minutes=1), # Slightly longer retry delay for management tasks + 'start_date': days_ago(1) +} + +# This single DAG contains operators for different management actions, +# This DAG allows adding URLs and then checking the status of the target queue. +with DAG( + dag_id='ytdlp_mgmt_queue_add_and_verify', # Updated DAG ID + default_args=default_args, + schedule_interval=None, # Manually triggered + catchup=False, + description='Manually add URLs to a YTDLP inbox queue and verify the queue status.', # Updated description + tags=['ytdlp', 'queue', 'management', 'redis', 'manual', 'add', 'verify'], # Updated tags + params={ + # Common params + 'redis_conn_id': Param(DEFAULT_REDIS_CONN_ID, type="string", description="Airflow Redis connection ID."), + # Params for adding URLs (and checking the same queue) + 'queue_name': Param(DEFAULT_QUEUE_NAME, type="string", title="Target Queue Name", description="Redis list (inbox queue) to add URLs to and check status of."), + 'urls': Param("", type="string", title="URLs to Add", description="Comma and/or newline separated list of video URLs.", multiline=True), # Updated description, keep multiline for UI + # Removed clear_queue_name param + # Removed check_queue_name param (will use queue_name) + } +) as dag: + + add_urls_task = PythonOperator( + task_id='add_urls_to_queue', + python_callable=add_urls_callable, + # Pass only relevant params to the callable via context['params'] + # Note: context['params'] automatically contains all DAG params + ) + add_urls_task.doc_md = """ + ### Add URLs to Queue + Adds URLs from the `urls` parameter (comma/newline separated) to the Redis list specified by `queue_name`. + *Trigger this task manually via the UI and provide the URLs.* + """ + + # Removed clear_queue_task + + check_status_task = PythonOperator( + task_id='check_queue_status_after_add', + python_callable=check_status_callable, + # No task-specific params needed; callable uses context['params'] directly. + ) + check_status_task.doc_md = """ + ### Check Queue Status After Add + Checks the type and length/size of the Redis key specified by `queue_name` (the same queue URLs were added to). + Logs the result and pushes `queue_key_type` and `queue_size` to XCom. + *This task runs automatically after `add_urls_to_queue`.* + """ + + # Define dependency: Add URLs first, then check status + add_urls_task >> check_status_task diff --git a/dags/ytdlp_mgmt_queue_check_status.py b/dags/ytdlp_mgmt_queue_check_status.py new file mode 100644 index 0000000..7033f70 --- /dev/null +++ b/dags/ytdlp_mgmt_queue_check_status.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright © 2024 rl +# +# Distributed under terms of the MIT license. + +""" +Airflow DAG for manually checking the status (type and size) of a specific Redis key used by YTDLP queues. +""" + +from airflow import DAG +from airflow.exceptions import AirflowException +from airflow.models.param import Param +from airflow.operators.python import PythonOperator +from airflow.providers.redis.hooks.redis import RedisHook +from airflow.utils.dates import days_ago +from datetime import timedelta +import logging +import redis # Import redis exceptions if needed + +# Configure logging +logger = logging.getLogger(__name__) + +# Default settings +DEFAULT_REDIS_CONN_ID = 'redis_default' +# Default to a common inbox pattern, user should override with the specific key +DEFAULT_QUEUE_TO_CHECK = 'video_queue_inbox' + +# --- Helper Function --- + +def _get_redis_client(redis_conn_id): + """Gets a Redis client connection using RedisHook.""" + try: + hook = RedisHook(redis_conn_id=redis_conn_id) + client = hook.get_conn() + client.ping() + logger.info(f"Successfully connected to Redis using connection '{redis_conn_id}'.") + return client + except redis.exceptions.AuthenticationError: + logger.error(f"Redis authentication failed for connection '{redis_conn_id}'. Check password.") + raise AirflowException(f"Redis authentication failed for '{redis_conn_id}'.") + except Exception as e: + logger.error(f"Failed to get Redis client for connection '{redis_conn_id}': {e}") + raise AirflowException(f"Redis connection failed for '{redis_conn_id}': {e}") + +# --- Python Callable for Check Status Task --- + +def check_status_callable(**context): + """Checks the length/size of the specified Redis key (queue/hash).""" + params = context['params'] + redis_conn_id = params['redis_conn_id'] + queue_to_check = params['queue_to_check'] # Specific queue/hash name + + if not queue_to_check: + raise ValueError("Parameter 'queue_to_check' cannot be empty.") + + logger.info(f"Attempting to check status of Redis key '{queue_to_check}' using connection '{redis_conn_id}'.") + try: + redis_client = _get_redis_client(redis_conn_id) + key_type = redis_client.type(queue_to_check) + key_type_str = key_type.decode('utf-8') if isinstance(key_type, bytes) else key_type # Decode if needed + + length = 0 + if key_type_str == 'list': + length = redis_client.llen(queue_to_check) + logger.info(f"Redis list '{queue_to_check}' has {length} items.") + elif key_type_str == 'hash': + length = redis_client.hlen(queue_to_check) + logger.info(f"Redis hash '{queue_to_check}' has {length} fields.") + elif key_type_str == 'none': + logger.info(f"Redis key '{queue_to_check}' does not exist.") + else: + # Attempt to get size for other types if possible, e.g., set size + try: + length = redis_client.scard(queue_to_check) # Example for set + logger.info(f"Redis key '{queue_to_check}' (type: {key_type_str}) has size {length}.") + except: + logger.info(f"Redis key '{queue_to_check}' exists but is of unhandled/unsizeable type '{key_type_str}'.") + + # Optionally push length to XCom if needed downstream + context['task_instance'].xcom_push(key='queue_key_type', value=key_type_str) + context['task_instance'].xcom_push(key='queue_size', value=length) + return {'key': queue_to_check, 'type': key_type_str, 'size': length} # Return status info + except Exception as e: + logger.error(f"Failed to check status of Redis key '{queue_to_check}': {e}", exc_info=True) + raise AirflowException(f"Failed to check Redis key status: {e}") + +# --- DAG Definition --- +default_args = { + 'owner': 'airflow', + 'depends_on_past': False, + 'email_on_failure': False, + 'email_on_retry': False, + 'retries': 1, + 'retry_delay': timedelta(seconds=30), + 'start_date': days_ago(1) +} + +with DAG( + dag_id='ytdlp_mgmt_queue_check_status', + default_args=default_args, + schedule_interval=None, # Manually triggered + catchup=False, + description='Manually check the type and size of a specific YTDLP Redis queue/key.', + tags=['ytdlp', 'queue', 'management', 'redis', 'manual', 'status'], + params={ + 'redis_conn_id': Param(DEFAULT_REDIS_CONN_ID, type="string", description="Airflow Redis connection ID."), + 'queue_to_check': Param( + DEFAULT_QUEUE_TO_CHECK, + type="string", + description="Exact name of the Redis key to check (e.g., 'video_queue_inbox_account_xyz', 'video_queue_progress', 'video_queue_result', 'video_queue_fail')." + ), + } +) as dag: + + check_status_task = PythonOperator( + task_id='check_specified_queue_status', + python_callable=check_status_callable, + # Params are implicitly passed via context['params'] + ) + check_status_task.doc_md = """ + ### Check Specified Queue/Key Status Task + Checks the type and size (length for lists, number of fields for hashes) of the Redis key specified by `queue_to_check`. + Logs the result and pushes `queue_key_type` and `queue_size` to XCom. + Can check keys like: + - `_inbox` (Redis List) + - `_progress` (Redis Hash) + - `_result` (Redis Hash) + - `_fail` (Redis Hash) + + *Trigger this task manually via the UI.* + """ diff --git a/dags/ytdlp_mgmt_queue_clear.py b/dags/ytdlp_mgmt_queue_clear.py new file mode 100644 index 0000000..50300c6 --- /dev/null +++ b/dags/ytdlp_mgmt_queue_clear.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright © 2024 rl +# +# Distributed under terms of the MIT license. + +""" +Airflow DAG for manually clearing (deleting) a specific Redis key used by YTDLP queues. +""" + +from airflow import DAG +from airflow.exceptions import AirflowException +from airflow.models.param import Param +from airflow.operators.python import PythonOperator +from airflow.providers.redis.hooks.redis import RedisHook +from airflow.utils.dates import days_ago +from datetime import timedelta +import logging +import redis # Import redis exceptions if needed + +# Configure logging +logger = logging.getLogger(__name__) + +# Default settings +DEFAULT_REDIS_CONN_ID = 'redis_default' +# Provide a placeholder default, user MUST specify the queue to clear +DEFAULT_QUEUE_TO_CLEAR = 'PLEASE_SPECIFY_QUEUE_TO_CLEAR' + +# --- Helper Function --- + +def _get_redis_client(redis_conn_id): + """Gets a Redis client connection using RedisHook.""" + try: + hook = RedisHook(redis_conn_id=redis_conn_id) + client = hook.get_conn() + client.ping() + logger.info(f"Successfully connected to Redis using connection '{redis_conn_id}'.") + return client + except redis.exceptions.AuthenticationError: + logger.error(f"Redis authentication failed for connection '{redis_conn_id}'. Check password.") + raise AirflowException(f"Redis authentication failed for '{redis_conn_id}'.") + except Exception as e: + logger.error(f"Failed to get Redis client for connection '{redis_conn_id}': {e}") + raise AirflowException(f"Redis connection failed for '{redis_conn_id}': {e}") + +# --- Python Callable for Clear Task --- + +def clear_queue_callable(**context): + """Clears (deletes) the specified Redis key (queue/hash).""" + params = context['params'] + redis_conn_id = params['redis_conn_id'] + queue_to_clear = params['queue_to_clear'] # Specific queue/hash name + + if not queue_to_clear or queue_to_clear == DEFAULT_QUEUE_TO_CLEAR: + raise ValueError("Parameter 'queue_to_clear' must be specified and cannot be the default placeholder.") + + logger.info(f"Attempting to clear Redis key '{queue_to_clear}' using connection '{redis_conn_id}'.") + try: + redis_client = _get_redis_client(redis_conn_id) + deleted_count = redis_client.delete(queue_to_clear) + if deleted_count > 0: + logger.info(f"Successfully cleared Redis key '{queue_to_clear}'.") + else: + logger.info(f"Redis key '{queue_to_clear}' did not exist or was already empty.") + except Exception as e: + logger.error(f"Failed to clear Redis key '{queue_to_clear}': {e}", exc_info=True) + raise AirflowException(f"Failed to clear Redis key: {e}") + +# --- DAG Definition --- +default_args = { + 'owner': 'airflow', + 'depends_on_past': False, + 'email_on_failure': False, + 'email_on_retry': False, + 'retries': 0, # No retries for manual clear operation + 'start_date': days_ago(1) +} + +with DAG( + dag_id='ytdlp_mgmt_queue_clear', + default_args=default_args, + schedule_interval=None, # Manually triggered + catchup=False, + description='Manually clear/delete a specific YTDLP Redis queue/key (inbox, progress, result, fail). Use with caution!', + tags=['ytdlp', 'queue', 'management', 'redis', 'manual', 'clear'], + params={ + 'redis_conn_id': Param(DEFAULT_REDIS_CONN_ID, type="string", description="Airflow Redis connection ID."), + 'queue_to_clear': Param( + DEFAULT_QUEUE_TO_CLEAR, + type="string", + description="Exact name of the Redis key to clear (e.g., 'video_queue_inbox_account_xyz', 'video_queue_progress', 'video_queue_result', 'video_queue_fail')." + ), + } +) as dag: + + clear_queue_task = PythonOperator( + task_id='clear_specified_queue', + python_callable=clear_queue_callable, + # Params are implicitly passed via context['params'] + ) + clear_queue_task.doc_md = """ + ### Clear Specified Queue/Key Task + Deletes the Redis key specified by the `queue_to_clear` parameter. + This can target any key, including: + - `_inbox` (Redis List): Contains URLs waiting to be processed. + - `_progress` (Redis Hash): Contains URLs currently being processed. + - `_result` (Redis Hash): Contains details of successfully processed URLs. + - `_fail` (Redis Hash): Contains details of failed URLs. + + **Warning:** This operation is destructive and cannot be undone. Ensure you specify the correct key name. + *Trigger this task manually via the UI.* + """ diff --git a/dags/ytdlp_mgmt_queue_list_contents.py b/dags/ytdlp_mgmt_queue_list_contents.py new file mode 100644 index 0000000..878580d --- /dev/null +++ b/dags/ytdlp_mgmt_queue_list_contents.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright © 2024 rl +# +# Distributed under terms of the MIT license. + +""" +Airflow DAG for manually listing the contents of a specific Redis key used by YTDLP queues. +""" + +from airflow import DAG +from airflow.exceptions import AirflowException +from airflow.models.param import Param +from airflow.operators.python import PythonOperator +from airflow.providers.redis.hooks.redis import RedisHook +from airflow.utils.dates import days_ago +from datetime import timedelta +import logging +import json +import redis # Import redis exceptions if needed + +# Configure logging +logger = logging.getLogger(__name__) + +# Default settings +DEFAULT_REDIS_CONN_ID = 'redis_default' +# Default to a common inbox pattern, user should override with the specific key +DEFAULT_QUEUE_TO_LIST = 'video_queue_inbox' +DEFAULT_MAX_ITEMS = 100 # Limit number of items listed by default + +# --- Helper Function --- + +def _get_redis_client(redis_conn_id): + """Gets a Redis client connection using RedisHook.""" + try: + hook = RedisHook(redis_conn_id=redis_conn_id) + # decode_responses=True removed as it's not supported by get_conn in some environments + # We will decode manually where needed. + client = hook.get_conn() + client.ping() + logger.info(f"Successfully connected to Redis using connection '{redis_conn_id}'.") + return client + except redis.exceptions.AuthenticationError: + logger.error(f"Redis authentication failed for connection '{redis_conn_id}'. Check password.") + raise AirflowException(f"Redis authentication failed for '{redis_conn_id}'.") + except Exception as e: + logger.error(f"Failed to get Redis client for connection '{redis_conn_id}': {e}") + raise AirflowException(f"Redis connection failed for '{redis_conn_id}': {e}") + +# --- Python Callable for List Contents Task --- + +def list_contents_callable(**context): + """Lists the contents of the specified Redis key (list or hash).""" + params = context['params'] + redis_conn_id = params['redis_conn_id'] + queue_to_list = params['queue_to_list'] + max_items = params.get('max_items', DEFAULT_MAX_ITEMS) + + if not queue_to_list: + raise ValueError("Parameter 'queue_to_list' cannot be empty.") + + logger.info(f"Attempting to list contents of Redis key '{queue_to_list}' (max: {max_items}) using connection '{redis_conn_id}'.") + try: + redis_client = _get_redis_client(redis_conn_id) + key_type_bytes = redis_client.type(queue_to_list) + key_type = key_type_bytes.decode('utf-8') # Decode type + + if key_type == 'list': + list_length = redis_client.llen(queue_to_list) + # Get range, respecting max_items (0 to max_items-1) + items_to_fetch = min(max_items, list_length) + # lrange returns list of bytes, decode each item + contents_bytes = redis_client.lrange(queue_to_list, 0, items_to_fetch - 1) + contents = [item.decode('utf-8') for item in contents_bytes] + logger.info(f"--- Contents of Redis List '{queue_to_list}' (showing first {len(contents)} of {list_length}) ---") + for i, item in enumerate(contents): + logger.info(f" [{i}]: {item}") # item is now a string + if list_length > len(contents): + logger.info(f" ... ({list_length - len(contents)} more items not shown)") + logger.info(f"--- End of List Contents ---") + # Optionally push contents to XCom if small enough + # context['task_instance'].xcom_push(key='list_contents', value=contents) + + elif key_type == 'hash': + hash_size = redis_client.hlen(queue_to_list) + # HGETALL can be risky for large hashes. Consider HSCAN for production. + # For manual inspection, HGETALL is often acceptable. + if hash_size > max_items * 2: # Heuristic: avoid huge HGETALL + logger.warning(f"Hash '{queue_to_list}' has {hash_size} fields, which is large. Listing might be slow or incomplete. Consider using redis-cli HSCAN.") + # Optionally implement HSCAN here for large hashes + # hgetall returns dict of bytes keys and bytes values, decode them + contents_bytes = redis_client.hgetall(queue_to_list) + contents = {k.decode('utf-8'): v.decode('utf-8') for k, v in contents_bytes.items()} + logger.info(f"--- Contents of Redis Hash '{queue_to_list}' ({len(contents)} fields) ---") + item_count = 0 + for key, value in contents.items(): # key and value are now strings + if item_count >= max_items: + logger.info(f" ... (stopped listing after {max_items} items of {hash_size})") + break + # Attempt to pretty-print if value is JSON + try: + parsed_value = json.loads(value) + pretty_value = json.dumps(parsed_value, indent=2) + logger.info(f" '{key}':\n{pretty_value}") + except json.JSONDecodeError: + logger.info(f" '{key}': {value}") # Print as string if not JSON + item_count += 1 + logger.info(f"--- End of Hash Contents ---") + # Optionally push contents to XCom if small enough + # context['task_instance'].xcom_push(key='hash_contents', value=contents) + + elif key_type == 'none': + logger.info(f"Redis key '{queue_to_list}' does not exist.") + else: + logger.info(f"Redis key '{queue_to_list}' is of type '{key_type}'. Listing contents for this type is not implemented.") + + except Exception as e: + logger.error(f"Failed to list contents of Redis key '{queue_to_list}': {e}", exc_info=True) + raise AirflowException(f"Failed to list Redis key contents: {e}") + +# --- DAG Definition --- +default_args = { + 'owner': 'airflow', + 'depends_on_past': False, + 'email_on_failure': False, + 'email_on_retry': False, + 'retries': 0, # No retries for manual list operation + 'start_date': days_ago(1) +} + +with DAG( + dag_id='ytdlp_mgmt_queue_list_contents', + default_args=default_args, + schedule_interval=None, # Manually triggered + catchup=False, + description='Manually list the contents of a specific YTDLP Redis queue/key (list or hash).', + tags=['ytdlp', 'queue', 'management', 'redis', 'manual', 'list'], + params={ + 'redis_conn_id': Param(DEFAULT_REDIS_CONN_ID, type="string", description="Airflow Redis connection ID."), + 'queue_to_list': Param( + DEFAULT_QUEUE_TO_LIST, + type="string", + description="Exact name of the Redis key (list/hash) to list contents for (e.g., 'video_queue_inbox_account_xyz', 'video_queue_progress', etc.)." + ), + 'max_items': Param(DEFAULT_MAX_ITEMS, type="integer", description="Maximum number of items/fields to list from the key."), + } +) as dag: + + list_contents_task = PythonOperator( + task_id='list_specified_queue_contents', + python_callable=list_contents_callable, + # Params are implicitly passed via context['params'] + ) + list_contents_task.doc_md = """ + ### List Specified Queue/Key Contents Task + Lists the contents of the Redis key specified by `queue_to_list`. + - For **Lists** (e.g., `_inbox`), shows the first `max_items`. + - For **Hashes** (e.g., `_progress`, `_result`, `_fail`), shows up to `max_items` key-value pairs. Attempts to pretty-print JSON values. + - Logs a warning for very large hashes. + + *Trigger this task manually via the UI.* + """ diff --git a/dags/ytdlp_proc_sequential_processor.py b/dags/ytdlp_proc_sequential_processor.py new file mode 100644 index 0000000..617c810 --- /dev/null +++ b/dags/ytdlp_proc_sequential_processor.py @@ -0,0 +1,910 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright © 2024 rl +# +# Distributed under terms of the MIT license. + +""" +DAG for processing YouTube URLs sequentially from a Redis queue using YTDLP Ops Thrift service. +""" + +from airflow import DAG +from airflow.exceptions import AirflowException, AirflowSkipException, AirflowFailException +from airflow.hooks.base import BaseHook +from airflow.models import BaseOperator, Variable +from airflow.models.param import Param +from airflow.operators.bash import BashOperator # Import BashOperator +from airflow.operators.python import PythonOperator +from airflow.providers.redis.hooks.redis import RedisHook +from airflow.utils.dates import days_ago +from airflow.utils.decorators import apply_defaults +from datetime import datetime, timedelta +from pangramia.yt.common.ttypes import TokenUpdateMode +from pangramia.yt.exceptions.ttypes import PBServiceException +from pangramia.yt.tokens_ops import YTTokenOpService +from thrift.protocol import TBinaryProtocol +from thrift.transport import TSocket, TTransport +from thrift.transport.TTransport import TTransportException +import json +import logging +import os +import redis # Import redis exceptions if needed +import socket +import time +import traceback # For logging stack traces in failure handler + +# Configure logging +logger = logging.getLogger(__name__) + +# Default settings +DEFAULT_QUEUE_NAME = 'video_queue' # Base name for queues +DEFAULT_REDIS_CONN_ID = 'redis_default' +DEFAULT_TIMEOUT = 30 # Default Thrift timeout in seconds +MAX_RETRIES_REDIS_LOOKUP = 3 # Retries for fetching service details from Redis +RETRY_DELAY_REDIS_LOOKUP = 10 # Delay (seconds) for Redis lookup retries + +# --- Helper Functions --- + +def _get_redis_client(redis_conn_id): + """Gets a Redis client connection using RedisHook.""" + try: + hook = RedisHook(redis_conn_id=redis_conn_id) + client = hook.get_conn() + client.ping() + logger.info(f"Successfully connected to Redis using connection '{redis_conn_id}'.") + return client + except redis.exceptions.AuthenticationError: + logger.error(f"Redis authentication failed for connection '{redis_conn_id}'. Check password.") + raise AirflowException(f"Redis authentication failed for '{redis_conn_id}'.") + except Exception as e: + logger.error(f"Failed to get Redis client for connection '{redis_conn_id}': {e}") + raise AirflowException(f"Redis connection failed for '{redis_conn_id}': {e}") + +def _extract_video_id(url): + """Extracts YouTube video ID from URL.""" + if not url or not isinstance(url, str): + logger.debug("URL is empty or not a string, cannot extract video ID.") + return None + try: + video_id = None + if 'youtube.com/watch?v=' in url: + video_id = url.split('v=')[1].split('&')[0] + elif 'youtu.be/' in url: + video_id = url.split('youtu.be/')[1].split('?')[0] + + if video_id and len(video_id) >= 11: + video_id = video_id[:11] # Standard ID length + logger.debug(f"Extracted video ID '{video_id}' from URL: {url}") + return video_id + else: + logger.debug(f"Could not extract a standard video ID pattern from URL: {url}") + return None + except Exception as e: + logger.error(f"Failed to extract video ID from URL '{url}'. Error: {e}") + return None + +# --- Queue Management Callables --- + +def pop_url_from_queue(**context): + """Pops a URL from the inbox queue and pushes to XCom.""" + params = context['params'] + queue_name = params['queue_name'] + inbox_queue = f"{queue_name}_inbox" + redis_conn_id = params.get('redis_conn_id', DEFAULT_REDIS_CONN_ID) + logger.info(f"Attempting to pop URL from inbox queue: {inbox_queue}") + + try: + client = _get_redis_client(redis_conn_id) + # LPOP is non-blocking, returns None if empty + url_bytes = client.lpop(inbox_queue) # Returns bytes if decode_responses=False on hook/client + + if url_bytes: + url = url_bytes.decode('utf-8') if isinstance(url_bytes, bytes) else url_bytes + logger.info(f"Popped URL: {url}") + context['task_instance'].xcom_push(key='current_url', value=url) + return url # Return URL for logging/potential use + else: + logger.info(f"Inbox queue '{inbox_queue}' is empty. Skipping downstream tasks.") + context['task_instance'].xcom_push(key='current_url', value=None) + # Raise AirflowSkipException to signal downstream tasks to skip + raise AirflowSkipException(f"Inbox queue '{inbox_queue}' is empty.") + except AirflowSkipException: + raise # Re-raise skip exception + except Exception as e: + logger.error(f"Error popping URL from Redis queue '{inbox_queue}': {e}", exc_info=True) + raise AirflowException(f"Failed to pop URL from Redis: {e}") + + +def move_url_to_progress(**context): + """Moves the current URL from XCom to the progress hash.""" + ti = context['task_instance'] + url = ti.xcom_pull(task_ids='pop_url_from_queue', key='current_url') + + # This task should be skipped if pop_url_from_queue raised AirflowSkipException + # Adding check for robustness + if not url: + logger.info("No URL found in XCom (or upstream skipped). Skipping move to progress.") + raise AirflowSkipException("No URL to process.") + + params = context['params'] + queue_name = params['queue_name'] + progress_queue = f"{queue_name}_progress" + redis_conn_id = params.get('redis_conn_id', DEFAULT_REDIS_CONN_ID) + logger.info(f"Moving URL '{url}' to progress hash: {progress_queue}") + + progress_data = { + 'status': 'processing', + 'start_time': time.time(), + 'dag_run_id': context['dag_run'].run_id, + 'task_instance_key_str': context['task_instance_key_str'] + } + + try: + client = _get_redis_client(redis_conn_id) + client.hset(progress_queue, url, json.dumps(progress_data)) + logger.info(f"Moved URL '{url}' to progress hash '{progress_queue}'.") + except Exception as e: + logger.error(f"Error moving URL to Redis progress hash '{progress_queue}': {e}", exc_info=True) + # If this fails, the URL is popped but not tracked as processing. Fail the task. + raise AirflowException(f"Failed to move URL to progress hash: {e}") + + +def handle_success(**context): + """Moves URL from progress to result hash on success.""" + ti = context['task_instance'] + url = ti.xcom_pull(task_ids='pop_url_from_queue', key='current_url') + if not url: + logger.warning("handle_success called but no URL found from pop_url_from_queue XCom. This shouldn't happen on success path.") + return # Or raise error + + params = context['params'] + queue_name = params['queue_name'] + progress_queue = f"{queue_name}_progress" + result_queue = f"{queue_name}_result" + redis_conn_id = params.get('redis_conn_id', DEFAULT_REDIS_CONN_ID) + + # Pull results from get_token task + info_json_path = ti.xcom_pull(task_ids='get_token', key='info_json_path') + socks_proxy = ti.xcom_pull(task_ids='get_token', key='socks_proxy') + ytdlp_command = ti.xcom_pull(task_ids='get_token', key='ytdlp_command') # Original command + + logger.info(f"Handling success for URL: {url}") + logger.info(f" Info JSON Path: {info_json_path}") + logger.info(f" SOCKS Proxy: {socks_proxy}") + logger.info(f" YTDLP Command: {ytdlp_command[:100] if ytdlp_command else 'None'}...") # Log truncated command + + result_data = { + 'status': 'success', + 'end_time': time.time(), + 'info_json_path': info_json_path, + 'socks_proxy': socks_proxy, + 'ytdlp_command': ytdlp_command, + 'url': url, + 'dag_run_id': context['dag_run'].run_id, + 'task_instance_key_str': context['task_instance_key_str'] # Record which task instance succeeded + } + + try: + client = _get_redis_client(redis_conn_id) + # Remove from progress hash + removed_count = client.hdel(progress_queue, url) + if removed_count > 0: + logger.info(f"Removed URL '{url}' from progress hash '{progress_queue}'.") + else: + logger.warning(f"URL '{url}' not found in progress hash '{progress_queue}' during success handling.") + + # Add to result hash + client.hset(result_queue, url, json.dumps(result_data)) + logger.info(f"Stored success result for URL '{url}' in result hash '{result_queue}'.") + + except Exception as e: + logger.error(f"Error handling success in Redis for URL '{url}': {e}", exc_info=True) + # Even if Redis fails, the task succeeded. Log error but don't fail the task. + # Consider adding retry logic for Redis operations here or marking state differently. + + +def handle_failure(**context): + """Moves URL from progress to fail hash on failure.""" + ti = context['task_instance'] + url = ti.xcom_pull(task_ids='pop_url_from_queue', key='current_url') + if not url: + logger.error("handle_failure called but no URL found from pop_url_from_queue XCom.") + # Cannot move to fail queue if URL is unknown + return + + params = context['params'] + queue_name = params['queue_name'] + progress_queue = f"{queue_name}_progress" + fail_queue = f"{queue_name}_fail" + redis_conn_id = params.get('redis_conn_id', DEFAULT_REDIS_CONN_ID) + + # Get failure reason from the exception context + exception = context.get('exception') + error_message = str(exception) if exception else "Unknown error" + # Get traceback if available + tb_str = traceback.format_exc() if exception else "No traceback available." + + logger.info(f"Handling failure for URL: {url}") + logger.error(f" Failure Reason: {error_message}") # Log the error that triggered failure + logger.debug(f" Traceback:\n{tb_str}") # Log traceback at debug level + + fail_data = { + 'status': 'failed', + 'end_time': time.time(), + 'error': error_message, + 'traceback': tb_str, # Store traceback + 'url': url, + 'dag_run_id': context['dag_run'].run_id, + 'task_instance_key_str': context['task_instance_key_str'] # Record which task instance failed + } + + try: + client = _get_redis_client(redis_conn_id) + # Remove from progress hash + removed_count = client.hdel(progress_queue, url) + if removed_count > 0: + logger.info(f"Removed URL '{url}' from progress hash '{progress_queue}'.") + else: + logger.warning(f"URL '{url}' not found in progress hash '{progress_queue}' during failure handling.") + + # Add to fail hash + client.hset(fail_queue, url, json.dumps(fail_data)) + logger.info(f"Stored failure details for URL '{url}' in fail hash '{fail_queue}'.") + + except Exception as e: + logger.error(f"Error handling failure in Redis for URL '{url}': {e}", exc_info=True) + # Log error, but the task already failed. + + +# --- YtdlpOpsOperator --- + +class YtdlpOpsOperator(BaseOperator): + """ + Custom Airflow operator to interact with YTDLP Thrift service. Handles direct connections + and Redis-based discovery, retrieves tokens, saves info.json, and manages errors. + Modified to pull URL from XCom for sequential processing. + """ + # Removed 'url' from template_fields as it's pulled from XCom + template_fields = ('service_ip', 'service_port', 'account_id', 'timeout', 'info_json_dir', 'redis_conn_id') + + @apply_defaults + def __init__(self, + # url parameter removed - will be pulled from XCom + redis_conn_id=DEFAULT_REDIS_CONN_ID, + max_retries_lookup=MAX_RETRIES_REDIS_LOOKUP, + retry_delay_lookup=RETRY_DELAY_REDIS_LOOKUP, + service_ip=None, + service_port=None, + redis_enabled=False, # Default to direct connection now + account_id=None, + # save_info_json removed, always True + info_json_dir=None, + # get_socks_proxy removed, always True + # store_socks_proxy removed, always True + # get_socks_proxy=True, # Removed + # store_socks_proxy=True, # Store proxy in XCom by default # Removed + timeout=DEFAULT_TIMEOUT, + *args, **kwargs): + super().__init__(*args, **kwargs) + + logger.info(f"Initializing YtdlpOpsOperator (Processor Version) with parameters: " + f"redis_conn_id={redis_conn_id}, max_retries_lookup={max_retries_lookup}, retry_delay_lookup={retry_delay_lookup}, " + f"service_ip={service_ip}, service_port={service_port}, redis_enabled={redis_enabled}, " + f"account_id={account_id}, info_json_dir={info_json_dir}, timeout={timeout}") + # save_info_json, get_socks_proxy, store_socks_proxy removed from log + + # Validate parameters based on connection mode + if redis_enabled: + # If using Redis, account_id is essential for lookup + if not account_id: + raise ValueError("account_id is required when redis_enabled=True for service lookup.") + else: + # If direct connection, IP and Port are essential + if not service_ip or not service_port: + raise ValueError("Both service_ip and service_port must be specified when redis_enabled=False.") + # Account ID is still needed for the API call itself, but rely on DAG param or operator config + if not account_id: + logger.warning("No account_id provided for direct connection mode. Ensure it's set in DAG params or operator config.") + # We won't assign 'default' here, let the value passed during instantiation be used. + + # self.url is no longer needed here + self.redis_conn_id = redis_conn_id + self.max_retries_lookup = max_retries_lookup + self.retry_delay_lookup = int(retry_delay_lookup.total_seconds() if isinstance(retry_delay_lookup, timedelta) else retry_delay_lookup) + self.service_ip = service_ip + self.service_port = service_port + self.redis_enabled = redis_enabled + self.account_id = account_id + # self.save_info_json removed + self.info_json_dir = info_json_dir # Still needed + # self.get_socks_proxy removed + # self.store_socks_proxy removed + self.timeout = timeout + + def execute(self, context): + logger.info("Executing YtdlpOpsOperator (Processor Version)") + transport = None + ti = context['task_instance'] # Get task instance for XCom access + + try: + # --- Get URL from XCom --- + url = ti.xcom_pull(task_ids='pop_url_from_queue', key='current_url') + if not url: + # This should ideally be caught by upstream skip, but handle defensively + logger.info("No URL found in XCom from pop_url_from_queue. Skipping execution.") + raise AirflowSkipException("Upstream task did not provide a URL.") + logger.info(f"Processing URL from XCom: {url}") + # --- End Get URL --- + + logger.info("Getting task parameters and rendering templates") + params = context['params'] # DAG run params + + # Render template fields using context + # Use render_template_as_native for better type handling if needed, else render_template + redis_conn_id = self.render_template(self.redis_conn_id, context) + service_ip = self.render_template(self.service_ip, context) + service_port_rendered = self.render_template(self.service_port, context) + account_id = self.render_template(self.account_id, context) + timeout_rendered = self.render_template(self.timeout, context) + info_json_dir = self.render_template(self.info_json_dir, context) # Rendered here for _save_info_json + + # Determine effective settings (DAG params override operator defaults) + redis_enabled = params.get('redis_enabled', self.redis_enabled) + account_id = params.get('account_id', account_id) # Use DAG param if provided + redis_conn_id = params.get('redis_conn_id', redis_conn_id) # Use DAG param if provided + + logger.info(f"Effective settings: redis_enabled={redis_enabled}, account_id='{account_id}', redis_conn_id='{redis_conn_id}'") + + host = None + port = None + + if redis_enabled: + # Get Redis connection using the helper for consistency + redis_client = _get_redis_client(redis_conn_id) + logger.info(f"Successfully connected to Redis using connection '{redis_conn_id}' for service discovery.") + + # Get service details from Redis with retries + service_key = f"ytdlp:{account_id}" + legacy_key = account_id # For backward compatibility + + for attempt in range(self.max_retries_lookup): + try: + logger.info(f"Attempt {attempt + 1}/{self.max_retries_lookup}: Fetching service details from Redis for keys: '{service_key}', '{legacy_key}'") + service_details = redis_client.hgetall(service_key) + if not service_details: + logger.warning(f"Key '{service_key}' not found, trying legacy key '{legacy_key}'") + service_details = redis_client.hgetall(legacy_key) + + if not service_details: + raise ValueError(f"No service details found in Redis for keys: {service_key} or {legacy_key}") + + # Find IP and port (case-insensitive keys) + ip_key = next((k for k in service_details if k.lower() == 'ip'), None) + port_key = next((k for k in service_details if k.lower() == 'port'), None) + + if not ip_key: raise ValueError(f"'ip' key not found in Redis hash for {service_key}/{legacy_key}") + if not port_key: raise ValueError(f"'port' key not found in Redis hash for {service_key}/{legacy_key}") + + host = service_details[ip_key] # Assumes decode_responses=True in hook + port_str = service_details[port_key] + + try: + port = int(port_str) + except (ValueError, TypeError): + raise ValueError(f"Invalid port value '{port_str}' found in Redis for {service_key}/{legacy_key}") + + logger.info(f"Extracted from Redis - Service IP: {host}, Service Port: {port}") + break # Success + + except Exception as e: + logger.warning(f"Attempt {attempt + 1} failed to get Redis details: {str(e)}") + if attempt == self.max_retries_lookup - 1: + logger.error("Max retries reached for fetching Redis details.") + raise AirflowException(f"Failed to get service details from Redis after {self.max_retries_lookup} attempts: {e}") + logger.info(f"Retrying in {self.retry_delay_lookup} seconds...") + time.sleep(self.retry_delay_lookup) + else: + # Direct connection: Use rendered/param values + host = params.get('service_ip', service_ip) # Use DAG param if provided + port_str = params.get('service_port', service_port_rendered) # Use DAG param if provided + + logger.info(f"Using direct connection settings: service_ip={host}, service_port={port_str}") + + if not host or not port_str: + raise ValueError("Direct connection requires service_ip and service_port (check Operator config and DAG params)") + try: + port = int(port_str) + except (ValueError, TypeError): + raise ValueError(f"Invalid service_port value: {port_str}") + + logger.info(f"Connecting directly to Thrift service at {host}:{port} (Redis bypassed)") + + # Validate and use timeout + try: + timeout = int(timeout_rendered) + if timeout <= 0: raise ValueError("Timeout must be positive") + logger.info(f"Using timeout: {timeout} seconds") + except (ValueError, TypeError): + logger.warning(f"Invalid timeout value: '{timeout_rendered}'. Using default: {DEFAULT_TIMEOUT}") + timeout = DEFAULT_TIMEOUT + + # Create Thrift connection objects + # socket_conn = TSocket.TSocket(host, port) # Original + socket_conn = TSocket.TSocket(host, port, socket_family=socket.AF_INET) # Explicitly use AF_INET (IPv4) + socket_conn.setTimeout(timeout * 1000) # Thrift timeout is in milliseconds + transport = TTransport.TFramedTransport(socket_conn) # Use TFramedTransport if server expects it + # transport = TTransport.TBufferedTransport(socket_conn) # Use TBufferedTransport if server expects it + protocol = TBinaryProtocol.TBinaryProtocol(transport) + client = YTTokenOpService.Client(protocol) + + logger.info(f"Attempting to connect to Thrift server at {host}:{port}...") + try: + transport.open() + logger.info("Successfully connected to Thrift server.") + + # Test connection with ping + try: + client.ping() + logger.info("Server ping successful.") + except Exception as e: + logger.error(f"Server ping failed: {e}") + raise AirflowException(f"Server connection test (ping) failed: {e}") + + # Get token from service using the URL from XCom + try: + logger.info(f"Requesting token for accountId='{account_id}', url='{url}'") + token_data = client.getOrRefreshToken( + accountId=account_id, + updateType=TokenUpdateMode.AUTO, + url=url # Use the url variable from XCom + ) + logger.info("Successfully retrieved token data from service.") + except PBServiceException as e: + # Handle specific service exceptions + error_code = getattr(e, 'errorCode', 'N/A') + error_message = getattr(e, 'message', 'N/A') + error_context = getattr(e, 'context', {}) + logger.error(f"PBServiceException occurred: Code={error_code}, Message={error_message}") + if error_context: + logger.error(f" Context: {error_context}") # Log context separately + # Construct a concise error message for AirflowException + error_msg = f"YTDLP service error (Code: {error_code}): {error_message}" + # Add specific error code handling if needed... + logger.error(f"Failing task instance due to PBServiceException: {error_msg}") # Add explicit log before raising + raise AirflowException(error_msg) # Fail task on service error + except TTransportException as e: + logger.error(f"Thrift transport error during getOrRefreshToken: {e}") + logger.error(f"Failing task instance due to TTransportException: {e}") # Add explicit log before raising + raise AirflowException(f"Transport error during API call: {e}") + except Exception as e: + logger.error(f"Unexpected error during getOrRefreshToken: {e}") + logger.error(f"Failing task instance due to unexpected error during API call: {e}") # Add explicit log before raising + raise AirflowException(f"Unexpected error during API call: {e}") + + except TTransportException as e: + # Handle connection errors + logger.error(f"Thrift transport error during connection: {str(e)}") + logger.error(f"Failing task instance due to TTransportException during connection: {e}") # Add explicit log before raising + raise AirflowException(f"Transport error connecting to YTDLP service: {str(e)}") + # Removed the overly broad except Exception block here, as inner blocks raise AirflowException + + # --- Process Token Data --- + logger.debug(f"Token data received. Attributes: {dir(token_data)}") + + info_json_path = None # Initialize + + # save_info_json is now always True + logger.info("Proceeding to save info.json (save_info_json=True).") + info_json = self._get_info_json(token_data) + if info_json and self._is_valid_json(info_json): + try: + # Pass rendered info_json_dir to helper + info_json_path = self._save_info_json(context, info_json, url, account_id, info_json_dir) + if info_json_path: + ti.xcom_push(key='info_json_path', value=info_json_path) + logger.info(f"Successfully saved info.json and pushed path to XCom: {info_json_path}") + else: + ti.xcom_push(key='info_json_path', value=None) + logger.warning("info.json saving failed (check logs from _save_info_json).") + except Exception as e: + logger.error(f"Unexpected error during info.json saving process: {e}", exc_info=True) + ti.xcom_push(key='info_json_path', value=None) + elif info_json: + logger.warning("Retrieved infoJson is not valid JSON. Skipping save.") + ti.xcom_push(key='info_json_path', value=None) + else: + logger.info("No infoJson found in token data. Skipping save.") + ti.xcom_push(key='info_json_path', value=None) + + + # Extract and potentially store SOCKS proxy + # get_socks_proxy and store_socks_proxy are now always True + socks_proxy = None + logger.info("Attempting to extract SOCKS proxy (get_socks_proxy=True).") + proxy_attr = next((attr for attr in ['socks5Proxy', 'socksProxy', 'socks'] if hasattr(token_data, attr)), None) + if proxy_attr: + socks_proxy = getattr(token_data, proxy_attr) + if socks_proxy: + logger.info(f"Extracted SOCKS proxy ({proxy_attr}): {socks_proxy}") + # Always store if found (store_socks_proxy=True) + ti.xcom_push(key='socks_proxy', value=socks_proxy) + logger.info("Pushed 'socks_proxy' to XCom.") + else: + logger.info(f"Found proxy attribute '{proxy_attr}' but value is empty.") + # Store None if attribute found but empty + ti.xcom_push(key='socks_proxy', value=None) + logger.info("Pushed None to XCom for 'socks_proxy' as extracted value was empty.") + else: + logger.info("No SOCKS proxy attribute found in token data.") + # Store None if attribute not found + ti.xcom_push(key='socks_proxy', value=None) + logger.info("Pushed None to XCom for 'socks_proxy' as attribute was not found.") + + +# --- Removed old logic block --- +# # Extract and potentially store SOCKS proxy +# socks_proxy = None +# get_socks_proxy = params.get('get_socks_proxy', self.get_socks_proxy) +# store_socks_proxy = params.get('store_socks_proxy', self.store_socks_proxy) +# +# if get_socks_proxy: +# proxy_attr = next((attr for attr in ['socks5Proxy', 'socksProxy', 'socks'] if hasattr(token_data, attr)), None) +# if proxy_attr: +# socks_proxy = getattr(token_data, proxy_attr) +# if socks_proxy: +# logger.info(f"Extracted SOCKS proxy ({proxy_attr}): {socks_proxy}") +# if store_socks_proxy: +# ti.xcom_push(key='socks_proxy', value=socks_proxy) +# logger.info("Pushed 'socks_proxy' to XCom.") +# else: +# logger.info(f"Found proxy attribute '{proxy_attr}' but value is empty.") +# if store_socks_proxy: ti.xcom_push(key='socks_proxy', value=None) +# else: +# logger.info("get_socks_proxy is True, but no SOCKS proxy attribute found.") +# if store_socks_proxy: ti.xcom_push(key='socks_proxy', value=None) +# else: +# logger.info("get_socks_proxy is False. Skipping proxy extraction.") +# if store_socks_proxy: ti.xcom_push(key='socks_proxy', value=None) +# --- End Removed old logic block --- + + + # Get the original command from the server + ytdlp_cmd = getattr(token_data, 'ytdlpCommand', None) + if not ytdlp_cmd: + logger.error("No 'ytdlpCommand' attribute found in token data.") + raise AirflowException("Required 'ytdlpCommand' not received from service.") + + logger.info(f"Original command received from server: {ytdlp_cmd[:100]}...") # Log truncated + + # Push the *original* command to XCom + ti.xcom_push(key='ytdlp_command', value=ytdlp_cmd) + logger.info("Pushed original command to XCom key 'ytdlp_command'.") + + # No explicit return needed, success is implicit if no exception raised + + except (AirflowSkipException, AirflowFailException) as e: + logger.info(f"Task skipped or failed explicitly: {e}") + raise # Re-raise to let Airflow handle state + except AirflowException as e: # Catch AirflowExceptions raised explicitly + logger.error(f"Operation failed due to AirflowException: {e}", exc_info=True) + raise # Re-raise AirflowExceptions to ensure task failure + except (TTransportException, PBServiceException) as e: # Catch specific Thrift/Service errors not already handled inside inner try + logger.error(f"Unhandled YTDLP Service/Transport error in outer block: {e}", exc_info=True) + logger.error(f"Failing task instance due to unhandled outer Service/Transport error: {e}") # Add explicit log before raising + raise AirflowException(f"Unhandled YTDLP service error: {e}") # Wrap in AirflowException to fail task + except Exception as e: # General catch-all for truly unexpected errors + logger.error(f"Caught unexpected error in YtdlpOpsOperator outer block: {e}", exc_info=True) + logger.error(f"Failing task instance due to unexpected outer error: {e}") # Add explicit log before raising + raise AirflowException(f"Unexpected error caused task failure: {e}") # Wrap to fail task + finally: + if transport and transport.isOpen(): + logger.info("Closing Thrift transport.") + transport.close() + + # --- Helper Methods --- + + def _get_info_json(self, token_data): + """Safely extracts infoJson from token data.""" + return getattr(token_data, 'infoJson', None) + + def _is_valid_json(self, json_str): + """Checks if a string is valid JSON.""" + if not json_str or not isinstance(json_str, str): return False + try: + json.loads(json_str) + return True + except json.JSONDecodeError: + return False + + def _save_info_json(self, context, info_json, url, account_id, rendered_info_json_dir): + """Saves info_json to a file. Uses pre-rendered directory path.""" + try: + video_id = _extract_video_id(url) # Use standalone helper + + save_dir = rendered_info_json_dir or "." # Use rendered path + logger.info(f"Target directory for info.json: {save_dir}") + + # Ensure directory exists + try: + os.makedirs(save_dir, exist_ok=True) + logger.info(f"Ensured directory exists: {save_dir}") + except OSError as e: + logger.error(f"Could not create directory {save_dir}: {e}. Cannot save info.json.") + return None + + # Construct filename + timestamp = int(time.time()) + base_filename = f"info_{video_id or 'unknown'}_{account_id}_{timestamp}.json" + info_json_path = os.path.join(save_dir, base_filename) + latest_json_path = os.path.join(save_dir, "latest.json") # Path for the latest symlink/copy + + # Write to timestamped file + try: + logger.info(f"Writing info.json content (received from service) to {info_json_path}...") + with open(info_json_path, 'w', encoding='utf-8') as f: + f.write(info_json) + logger.info(f"Successfully saved info.json to timestamped file: {info_json_path}") + except IOError as e: + logger.error(f"Failed to write info.json to {info_json_path}: {e}") + return None + + # Write to latest.json (overwrite) - best effort + try: + with open(latest_json_path, 'w', encoding='utf-8') as f: + f.write(info_json) + logger.info(f"Updated latest.json file: {latest_json_path}") + except IOError as e: + logger.warning(f"Failed to update latest.json at {latest_json_path}: {e}") + + return info_json_path + + except Exception as e: + logger.error(f"Unexpected error in _save_info_json: {e}", exc_info=True) + return None + + +# ============================================================================= +# DAG Definition +# ============================================================================= + +default_args = { + 'owner': 'airflow', + 'depends_on_past': False, + 'email_on_failure': False, + 'email_on_retry': False, + 'retries': 1, # Default retries for tasks like queue management + 'retry_delay': timedelta(minutes=1), + 'start_date': days_ago(1), + # Add concurrency control if needed for sequential processing + # 'concurrency': 1, # Ensure only one task instance runs at a time per DAG run + # 'max_active_runs': 1, # Ensure only one DAG run is active +} + +# Define DAG +with DAG( + dag_id='ytdlp_proc_sequential_processor', # New DAG ID + default_args=default_args, + schedule_interval=None, # Manually triggered or triggered by external sensor/event + catchup=False, + description='Processes YouTube URLs sequentially from a Redis queue using YTDLP Ops.', + tags=['ytdlp', 'thrift', 'client', 'sequential', 'queue', 'processor'], # Updated tags + params={ + # Define DAG parameters + 'queue_name': Param(DEFAULT_QUEUE_NAME, type="string", description="Base name for Redis queues (e.g., 'video_queue' -> video_queue_inbox, video_queue_progress, etc.)."), + 'redis_conn_id': Param(DEFAULT_REDIS_CONN_ID, type="string", description="Airflow Redis connection ID."), + # YtdlpOpsOperator specific params (can be overridden at task level if needed) + 'redis_enabled': Param(False, type="boolean", description="Use Redis for service discovery? If False, uses service_ip/port."), # Default changed to False + 'service_ip': Param(None, type=["null", "string"], description="Required Service IP if redis_enabled=False."), # Clarified requirement + 'service_port': Param(None, type=["null", "integer"], description="Required Service port if redis_enabled=False."), # Clarified requirement + 'account_id': Param('default_account', type="string", description="Account ID for the API call (used for Redis lookup if redis_enabled=True)."), # Clarified usage + 'timeout': Param(DEFAULT_TIMEOUT, type="integer", description="Timeout in seconds for the Thrift connection."), + # save_info_json removed, always True + # get_socks_proxy removed, always True + # store_socks_proxy removed, always True + # Download specific parameters + 'download_format': Param( + # Default to best audio-only format (e.g., m4a) + 'ba[ext=m4a]/bestaudio/best', + type="string", + description="yt-dlp format selection string (e.g., 'ba' for best audio, 'wv*+wa/w' for worst video+audio)." + ), + 'output_path_template': Param( + # Simplified template, removed queue_name subdir + "{{ var.value.get('DOWNLOADS_TEMP', '/opt/airflow/downloads') }}/%(title)s [%(id)s].%(ext)s", + type="string", + description="yt-dlp output template (e.g., '/path/to/downloads/%(title)s.%(ext)s'). Uses Airflow Variable 'DOWNLOADS_TEMP'." + ), + # Simplified info_json_dir, just uses DOWNLOADS_TEMP variable + 'info_json_dir': Param( + "{{ var.value.get('DOWNLOADS_TEMP', '/opt/airflow/downloadfiles') }}", + type="string", + description="Directory to save info.json. Uses Airflow Variable 'DOWNLOADS_TEMP'." + ) + } +) as dag: + + # --- Task Definitions --- + + pop_url = PythonOperator( + task_id='pop_url_from_queue', + python_callable=pop_url_from_queue, + # Params are implicitly passed via context + ) + pop_url.doc_md = """ + ### Pop URL from Inbox Queue + Pops the next available URL from the `{{ params.queue_name }}_inbox` Redis list. + Pushes the URL to XCom key `current_url`. + If the queue is empty, raises `AirflowSkipException` to skip downstream tasks. + """ + + move_to_progress = PythonOperator( + task_id='move_url_to_progress', + python_callable=move_url_to_progress, + trigger_rule='all_success', # Only run if pop_url succeeded (didn't skip) + ) + move_to_progress.doc_md = """ + ### Move URL to Progress Hash + Retrieves the `current_url` from XCom (pushed by `pop_url_from_queue`). + Adds the URL as a key to the `{{ params.queue_name }}_progress` Redis hash with status 'processing'. + This task is skipped if `pop_url_from_queue` was skipped. + """ + + # YtdlpOpsOperator task to get the token + get_token = YtdlpOpsOperator( + task_id='get_token', + # Operator params are inherited from DAG params by default, + # but can be overridden here if needed. + # We rely on the operator pulling the URL from XCom internally. + # Pass DAG params explicitly to ensure they are used if overridden + redis_conn_id="{{ params.redis_conn_id }}", + redis_enabled="{{ params.redis_enabled }}", + service_ip="{{ params.service_ip }}", + service_port="{{ params.service_port }}", + account_id="{{ params.account_id }}", + timeout="{{ params.timeout }}", + # save_info_json removed + info_json_dir="{{ params.info_json_dir }}", # Pass the simplified path template + # get_socks_proxy removed + # store_socks_proxy removed + retries=0, # Set operator retries to 0; failure handled by branching/failure handler + trigger_rule='all_success', # Only run if move_to_progress succeeded + ) + get_token.doc_md = """ + ### Get Token and Info Task + Connects to the YTDLP Thrift service for the URL pulled from XCom (`current_url`). + Retrieves token, metadata, command, and potentially proxy. Saves `info.json`. + Failure of this task triggers the `handle_failure` path. + Success triggers the `handle_success` path. + + **Pulls from XCom:** + - `current_url` (from `pop_url_from_queue`) - *Used internally* + + **Pushes to XCom:** + - `info_json_path` + - `socks_proxy` + - `ytdlp_command` + """ + + # Task to perform the actual download using yt-dlp + # Ensure info_json_path and socks_proxy are correctly quoted within the bash command + # Use {% raw %} {% endraw %} around Jinja if needed, but direct templating should work here. + # Added --no-simulate, --no-write-info-json, --ignore-errors, --no-progress + download_video = BashOperator( + task_id='download_video', + bash_command=""" + INFO_JSON_PATH="{{ ti.xcom_pull(task_ids='get_token', key='info_json_path') }}" + PROXY="{{ ti.xcom_pull(task_ids='get_token', key='socks_proxy') }}" + FORMAT="{{ params.download_format }}" + OUTPUT_TEMPLATE="{{ params.output_path_template }}" + + echo "Starting download..." + echo "Info JSON Path: $INFO_JSON_PATH" + echo "Proxy: $PROXY" + echo "Format: $FORMAT" + echo "Output Template: $OUTPUT_TEMPLATE" + + # Check if info.json path exists + if [ -z "$INFO_JSON_PATH" ] || [ ! -f "$INFO_JSON_PATH" ]; then + echo "Error: info.json path is missing or file does not exist ($INFO_JSON_PATH)." + exit 1 + fi + + # Construct command + CMD="yt-dlp --load-info-json \"$INFO_JSON_PATH\"" + + # Add proxy if it exists + if [ -n "$PROXY" ]; then + CMD="$CMD --proxy \"$PROXY\"" + fi + + # Add format and output template + CMD="$CMD -f \"$FORMAT\" -o \"$OUTPUT_TEMPLATE\"" + + # Add other useful flags + CMD_ARRAY=(yt-dlp --load-info-json "$INFO_JSON_PATH") + + # Add proxy if it exists + if [ -n "$PROXY" ]; then + CMD_ARRAY+=(--proxy "$PROXY") + fi + + # Add format and output template + CMD_ARRAY+=(-f "$FORMAT" -o "$OUTPUT_TEMPLATE") + + # Add other useful flags + CMD_ARRAY+=(--no-progress --no-simulate --no-write-info-json --ignore-errors --verbose) + + echo "Executing command array:" + # Use printf to safely quote and display the command array + printf "%q " "${CMD_ARRAY[@]}" + echo "" # Newline after command + + # Execute the command directly using the array + "${CMD_ARRAY[@]}" + + # Check exit code + EXIT_CODE=$? + if [ $EXIT_CODE -ne 0 ]; then + echo "Error: yt-dlp command failed with exit code $EXIT_CODE" + exit $EXIT_CODE + fi + echo "Download command completed successfully." + """, + trigger_rule='all_success', # Run only if get_token succeeded + ) + download_video.doc_md = """ + ### Download Video/Audio Task + Executes `yt-dlp` using the `info.json` and proxy obtained from the `get_token` task. + Uses the `download_format` and `output_path_template` parameters from the DAG run configuration. + Failure of this task triggers the `handle_failure` path. + + **Pulls from XCom (task_id='get_token'):** + - `info_json_path` + - `socks_proxy` + """ + + + # Task to handle successful token retrieval AND download + success_handler = PythonOperator( + task_id='handle_success', + python_callable=handle_success, + trigger_rule='all_success', # Run only if get_token succeeds + ) + success_handler.doc_md = """ + ### Handle Success Task + Runs after `get_token` succeeds. + Retrieves `current_url` and results from `get_token` via XCom. + Removes the URL from the `{{ params.queue_name }}_progress` hash. + Adds the URL and results to the `{{ params.queue_name }}_result` hash. + """ + + # Task to handle failed token retrieval or download + failure_handler = PythonOperator( + task_id='handle_failure', + python_callable=handle_failure, + trigger_rule='one_failed', # Run only if get_token or download_video fails + ) + failure_handler.doc_md = """ + ### Handle Failure Task + # Runs after `get_token` (or potentially `move_url_to_progress`) fails. + # Retrieves `current_url` from XCom. + # Retrieves the error message and traceback from the context. + # Removes the URL from the `{{ params.queue_name }}_progress` hash. + # Adds the URL and error details to the `{{ params.queue_name }}_fail` hash. + # **Important:** This task succeeding means the failure was *handled*, the DAG run itself might still be marked as failed if `get_token` failed. + # """ + + + # --- Task Dependencies --- + # Core processing flow + pop_url >> move_to_progress >> get_token >> download_video + + # Handlers depend on the outcome of both token retrieval and download + # Success handler runs only if download_video succeeds + download_video >> success_handler # Default trigger_rule='all_success' is suitable + + # Failure handler runs if either get_token or download_video fails + [get_token, download_video] >> failure_handler # Uses trigger_rule='one_failed' defined in the task + +# Removed Jinja filters as they are no longer needed for the simplified info_json_dir diff --git a/docker-compose-ytdlp-ops.yaml b/docker-compose-ytdlp-ops.yaml index cc3f57e..ff7a153 100644 --- a/docker-compose-ytdlp-ops.yaml +++ b/docker-compose-ytdlp-ops.yaml @@ -1,15 +1,40 @@ version: '3.8' services: - ytdlp-ops: - image: pangramia/ytdlp-ops-server:latest + camoufox: + build: + context: ./camoufox # Path relative to the docker-compose file + dockerfile: Dockerfile ports: - - "9090:9090" - - "9091:9091" + # Optionally expose the camoufox port to the host for debugging + # - "12345:12345" + - "12345" # Expose port within the docker network, pass in Dockerfile + - "5900:5900" # Expose VNC port to the host + networks: + - airflow_prod_proxynet + command: [ + "--ws-host", "0.0.0.0", + "--port", "12345", + "--ws-path", "mypath", + "--proxy-url", "socks5://sslocal-rust-1082:1082", + "--locale", "en-US", + "--geoip", + "--extensions", "/app/extensions/google_sign_in_popup_blocker-1.0.2.xpi,/app/extensions/spoof_timezone-0.3.4.xpi,/app/extensions/youtube_ad_auto_skipper-0.6.0.xpi" + ] + restart: unless-stopped + # Add healthcheck if desired + + ytdlp-ops: + image: pangramia/ytdlp-ops-server:latest # Don't comment + depends_on: + - camoufox # Ensure camoufox starts first + ports: + - "9090:9090" # Main RPC port + - "9091:9091" # Health check port volumes: - context-data:/app/context-data networks: - - airflow_workers_prod_proxynet + - airflow_prod_proxynet command: - "--script-dir" - "/app/scripts" @@ -18,10 +43,18 @@ services: - "--port" - "9090" - "--clients" - - "ios,android,mweb" + # Add 'web' client since we now have camoufox + - "web,ios,android,mweb" - "--proxy" - - "socks5://sslocal-rust-1084:1084" + - "socks5://sslocal-rust-1082:1082" + # Add the endpoint argument pointing to the camoufox service + - "--endpoint" + - "ws://camoufox:12345/mypath" - "--probe" + # Add --camouflage-only if you don't want ytdlp-ops to manage the browser directly + - "--camouflage-only" + # Add flag to print full tokens in logs by default + - "--print-tokens" restart: unless-stopped pull_policy: always @@ -30,5 +63,5 @@ volumes: name: context-data networks: - airflow_workers_prod_proxynet: + airflow_prod_proxynet: external: true diff --git a/ytdlp-ops-auth/info_json_vKTVLpmvznI.json b/ytdlp-ops-auth/info_json_vKTVLpmvznI.json deleted file mode 120000 index 1f81720..0000000 --- a/ytdlp-ops-auth/info_json_vKTVLpmvznI.json +++ /dev/null @@ -1 +0,0 @@ -info_json_vKTVLpmvznI_1743507631.json \ No newline at end of file diff --git a/ytdlp-ops-auth/info_json_vKTVLpmvznI_1743507631.json b/ytdlp-ops-auth/info_json_vKTVLpmvznI_1743507631.json deleted file mode 100644 index 4a87bfb..0000000 --- a/ytdlp-ops-auth/info_json_vKTVLpmvznI_1743507631.json +++ /dev/null @@ -1,1474 +0,0 @@ -{ - "id": "vKTVLpmvznI", - "title": "Best of Dmitri Shostakovich - Essential Classical Music", - "formats": [ - { - "format_id": "sb3", - "format_note": "storyboard", - "ext": "mhtml", - "protocol": "mhtml", - "acodec": "none", - "vcodec": "none", - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCNceDOVve20wuvVEw7USJxQaljmA", - "width": 48, - "height": 27, - "fps": 0.00803923144947343, - "rows": 10, - "columns": 10, - "fragments": [ - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCNceDOVve20wuvVEw7USJxQaljmA", - "duration": 12439.0 - } - ], - "audio_ext": "none", - "video_ext": "none", - "vbr": 0, - "abr": 0, - "tbr": null, - "resolution": "48x27", - "aspect_ratio": 1.78, - "filesize_approx": null, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "sb3 - 48x27 (storyboard)" - }, - { - "format_id": "sb2", - "format_note": "storyboard", - "ext": "mhtml", - "protocol": "mhtml", - "acodec": "none", - "vcodec": "none", - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "width": 80, - "height": 45, - "fps": 0.10008843154594421, - "rows": 10, - "columns": 10, - "fragments": [ - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M0.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M1.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M2.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M3.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M4.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M5.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M6.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M7.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M8.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M9.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M10.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M11.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M12.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 449.6024096385536 - } - ], - "audio_ext": "none", - "video_ext": "none", - "vbr": 0, - "abr": 0, - "tbr": null, - "resolution": "80x45", - "aspect_ratio": 1.78, - "filesize_approx": null, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "sb2 - 80x45 (storyboard)" - }, - { - "format_id": "sb1", - "format_note": "storyboard", - "ext": "mhtml", - "protocol": "mhtml", - "acodec": "none", - "vcodec": "none", - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "width": 160, - "height": 90, - "fps": 0.10008843154594421, - "rows": 5, - "columns": 5, - "fragments": [ - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M0.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M1.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M2.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M3.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M4.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M5.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M6.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M7.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M8.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M9.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M10.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M11.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M12.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M13.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M14.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M15.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M16.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M17.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M18.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M19.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M20.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M21.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M22.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M23.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M24.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M25.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M26.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M27.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M28.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M29.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M30.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M31.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M32.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M33.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M34.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M35.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M36.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M37.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M38.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M39.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M40.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M41.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M42.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M43.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M44.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M45.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M46.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M47.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M48.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M49.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 199.82329317269068 - } - ], - "audio_ext": "none", - "video_ext": "none", - "vbr": 0, - "abr": 0, - "tbr": null, - "resolution": "160x90", - "aspect_ratio": 1.78, - "filesize_approx": null, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "sb1 - 160x90 (storyboard)" - }, - { - "format_id": "sb0", - "format_note": "storyboard", - "ext": "mhtml", - "protocol": "mhtml", - "acodec": "none", - "vcodec": "none", - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "width": 320, - "height": 180, - "fps": 0.10008843154594421, - "rows": 3, - "columns": 3, - "fragments": [ - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M0.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M1.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M2.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M3.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M4.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M5.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M6.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M7.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M8.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M9.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M10.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M11.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M12.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M13.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M14.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M15.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M16.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M17.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M18.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M19.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M20.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M21.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M22.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M23.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M24.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M25.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M26.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M27.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M28.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M29.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M30.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M31.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M32.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M33.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M34.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M35.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M36.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M37.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M38.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M39.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M40.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M41.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M42.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M43.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M44.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M45.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M46.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M47.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M48.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M49.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M50.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M51.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M52.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M53.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M54.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M55.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M56.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M57.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M58.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M59.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M60.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M61.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M62.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M63.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M64.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M65.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M66.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M67.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M68.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M69.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M70.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M71.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M72.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M73.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M74.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M75.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M76.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M77.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M78.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M79.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M80.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M81.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M82.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M83.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M84.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M85.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M86.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M87.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M88.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M89.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M90.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M91.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M92.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M93.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M94.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M95.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M96.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M97.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M98.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M99.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M100.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M101.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M102.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M103.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M104.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M105.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M106.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M107.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M108.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M109.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M110.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M111.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M112.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M113.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M114.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M115.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M116.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M117.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M118.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M119.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M120.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M121.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M122.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M123.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M124.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M125.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M126.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M127.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M128.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M129.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M130.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M131.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M132.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M133.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M134.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M135.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M136.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M137.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M138.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 29.973493975903693 - } - ], - "audio_ext": "none", - "video_ext": "none", - "vbr": 0, - "abr": 0, - "tbr": null, - "resolution": "320x180", - "aspect_ratio": 1.78, - "filesize_approx": null, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "sb0 - 320x180 (storyboard)" - }, - { - "asr": 44100, - "filesize": 401472565, - "format_id": "18", - "format_note": "360p, WEB", - "source_preference": -1, - "fps": 25, - "audio_channels": 2, - "height": 360, - "quality": 6.0, - "has_drm": false, - "tbr": 258.192, - "filesize_approx": 401471228, - "url": "https://rr5---sn-4g5edndz.googlevideo.com/videoplayback?expire=1743529225&ei=qdDrZ-LsKoH54-EP2PCkqQg&ip=79.137.202.19&id=o-AECkHm_LNzZwxiB7DWgZ8xD1oTygyyn0lqkf8gis7zLD&itag=18&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&met=1743507625%2C&mh=k5&mm=31%2C26&mn=sn-4g5edndz%2Csn-h0jeener&ms=au%2Conr&mv=m&mvi=5&pl=23&rms=au%2Cau&initcwndbps=1266250&bui=AccgBcP5u60QMh-eqPBu3EhQFDx5iW0GHP48OVcWCQvImxZdHHOPrnwHlxiCQ6BcyiAEoZ1dCPexFX03&spc=_S3wKlc5vLQyAwz48PlBCy5ej7_4zYJhl12k4Oug_Nhji995VJmceRP6jKVKCcneIEB5Lkl94A&vprv=1&svpuc=1&mime=video%2Fmp4&ns=TQbcu0FMoR96nxJoch6pqrgQ&rqh=1&gir=yes&clen=401472565&ratebypass=yes&dur=12439.463&lmt=1743385980965512&mt=1743507195&fvip=3&fexp=51355912%2C51435733&c=WEB&sefc=1&txp=4438534&n=MZTYGnga5TOGoA&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&lsparams=met%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Crms%2Cinitcwndbps&lsig=AFVRHeAwRAIgbq4n7-4EohQ39k0IvhhaAtlVFSfOumJpwaKa5fqP2WICIBDx3yAKFs152Tw-HV5REwBGHOYPocxoiHTlp6UdOBXA&sig=AJfQdSswRgIhALPnRNBR401MCvj3BHaAP7tz1666bRiXjTU8XnhCM5EhAiEAt9rzbdV9bT1mOKd57NA9xdKdeQGWlyqJoAdzUsFctWg%3D", - "width": 640, - "language": null, - "language_preference": -1, - "preference": null, - "ext": "mp4", - "vcodec": "avc1.42001E", - "acodec": "mp4a.40.2", - "dynamic_range": "SDR", - "downloader_options": { - "http_chunk_size": 10485760 - }, - "protocol": "https", - "video_ext": "mp4", - "audio_ext": "none", - "vbr": null, - "abr": null, - "resolution": "640x360", - "aspect_ratio": 1.78, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "18 - 640x360 (360p, WEB)" - } - ], - "thumbnails": [ - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/3.jpg", - "preference": -37, - "id": "0" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/3.webp", - "preference": -36, - "id": "1" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/2.jpg", - "preference": -35, - "id": "2" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/2.webp", - "preference": -34, - "id": "3" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/1.jpg", - "preference": -33, - "id": "4" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/1.webp", - "preference": -32, - "id": "5" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/mq3.jpg", - "preference": -31, - "id": "6" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/mq3.webp", - "preference": -30, - "id": "7" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/mq2.jpg", - "preference": -29, - "id": "8" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/mq2.webp", - "preference": -28, - "id": "9" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/mq1.jpg", - "preference": -27, - "id": "10" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/mq1.webp", - "preference": -26, - "id": "11" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hq3.jpg", - "preference": -25, - "id": "12" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/hq3.webp", - "preference": -24, - "id": "13" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hq2.jpg", - "preference": -23, - "id": "14" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/hq2.webp", - "preference": -22, - "id": "15" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hq1.jpg", - "preference": -21, - "id": "16" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/hq1.webp", - "preference": -20, - "id": "17" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/sd3.jpg", - "preference": -19, - "id": "18" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/sd3.webp", - "preference": -18, - "id": "19" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/sd2.jpg", - "preference": -17, - "id": "20" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/sd2.webp", - "preference": -16, - "id": "21" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/sd1.jpg", - "preference": -15, - "id": "22" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/sd1.webp", - "preference": -14, - "id": "23" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/default.jpg", - "preference": -13, - "id": "24" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/default.webp", - "preference": -12, - "id": "25" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/mqdefault.jpg", - "preference": -11, - "id": "26" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/mqdefault.webp", - "preference": -10, - "id": "27" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/0.jpg", - "preference": -9, - "id": "28" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/0.webp", - "preference": -8, - "id": "29" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hqdefault.jpg", - "preference": -7, - "id": "30" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLCWGGqXfZOyDXVDblw-T6EV_UmHOw", - "height": 94, - "width": 168, - "preference": -7, - "id": "31", - "resolution": "168x94" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLCc2LZi4OgeI7BJQMT5IKNWi6B9VQ", - "height": 110, - "width": 196, - "preference": -7, - "id": "32", - "resolution": "196x110" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAvvAjHqsZKN97OayOaKmjUsuYfuA", - "height": 138, - "width": 246, - "preference": -7, - "id": "33", - "resolution": "246x138" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLA3kWM8Xrcnk0DKdMWwnyD1b3Kn7g", - "height": 188, - "width": 336, - "preference": -7, - "id": "34", - "resolution": "336x188" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/hqdefault.webp", - "preference": -6, - "id": "35" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/sddefault.jpg", - "preference": -5, - "id": "36" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/sddefault.webp", - "preference": -4, - "id": "37" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hq720.jpg", - "preference": -3, - "id": "38" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/hq720.webp", - "preference": -2, - "id": "39" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/maxresdefault.jpg", - "height": 1080, - "width": 1920, - "preference": -1, - "id": "40", - "resolution": "1920x1080" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/maxresdefault.webp", - "preference": 0, - "id": "41" - } - ], - "thumbnail": "https://i.ytimg.com/vi/vKTVLpmvznI/maxresdefault.jpg", - "description": "Shostakovich\u2019s music is a testament to resilience, defiance, and profound emotion. His compositions, marked by striking contrasts and deep intensity, reflect both the struggles of his time and the brilliance of his creative mind. From hauntingly beautiful adagios to electrifying symphonic movements, his work carries a unique voice that speaks of both sorrow and triumph.\n\nThis collection brings together some of his most powerful and evocative works, showcasing his mastery of orchestration, his sharp wit, and his deep understanding of human emotion. Whether it\u2019s the raw intensity of his symphonies, the elegance of his chamber music, or the unsettling beauty of his piano pieces, every note is charged with meaning.\n\n\ud83d\udfe2 Follow us and our playlists on Spotify:\nhttps://open.spotify.com/user/cugate-music?si=2d9adbc210b64a2b\n\n\ud83d\udc49 Listen to classical music on other streaming platforms. The links are below: \n\n\ud83d\udccc \ud835\udc13\ud835\udc21\ud835\udc1e \ud835\udfcf\ud835\udfce\ud835\udfce \ud835\udc01\ud835\udc1e\ud835\udc2c\ud835\udc2d \ud835\udc0e\ud835\udc1f \ud835\udc02\ud835\udc25\ud835\udc1a\ud835\udc2c\ud835\udc2c\ud835\udc22\ud835\udc1c: https://lnk.to/Various-Artists-The-100-Best-Of-Classic-Volume-2\n\ud83d\udccc \ud835\udc01\ud835\udc1e\ud835\udc2c\ud835\udc2d \ud835\udc28\ud835\udc1f \ud835\udc03\ud835\udc1e\ud835\udc1b\ud835\udc2e\ud835\udc2c\ud835\udc2c\ud835\udc32: https://lnk.to/Various-Artists-Best-of-Debussy\n\ud83d\udccc \ud835\udc01\ud835\udc1e\ud835\udc2c\ud835\udc2d \ud835\udc28\ud835\udc1f \ud835\udc07\ud835\udc1a\ud835\udc27\ud835\udc1d\ud835\udc1e\ud835\udc25: https://lnk.to/Various-Artists-Best-of-Handel\n\n\n\ud835\udc0f\ud835\udc0b\ud835\udc00\ud835\udc18\ud835\udc0b\ud835\udc08\ud835\udc12\ud835\udc13\n--------------------\n00:00:00 Prelude No. 17, Op. 34 (6)\n00:02:14 Piano Concerto No. 1, Op. 35: II. Lento (6)\n00:09:25 The Gadfly Suite, Op. 97a: II. Contredanse (4)\n00:12:35 Hamlet - Suite from the Film, Op. 116 A: IV. In the Garden (4)\n00:15:15 The Gadfly Suite, Op. 97a: V. Barrel Organ Waltz (4)\n00:17:27 The Gadfly Suite, Op. 97a: IV. Interlude (4)\n00:20:44 String Quartet No. 4, Op. 83: IV. Allegretto (4)\n00:29:33 The Gadfly Suite, Op. 97a: VI. Galop (4)\n00:31:22 The Gadfly Suite, Op. 97a: VII. Introduction (4)\n00:38:11 The Gadfly Suite, Op. 97a: VIII. Romance (4)\n00:45:28 The Gadfly Suite, Op. 97a: IX. Intermezzo (4)\n00:51:18 The Gadfly Suite, Op. 97a: X. Nocturne (4)\n00:55:23 The Gadfly Suite, Op. 97a: XI. Scene (4)\n00:58:18 Hamlet - Suite from the Film, Op. 116 A: VII. Ophelia (4)\n01:02:47 String Quartet No. 4, Op. 83: I. Allegretto (4)\n01:07:54 String Quartet No. 4, Op. 83: II. Andantino (4)\n01:15:55 String Quartet No. 5 in B-Flat Major, Op. 92: II. Andante - Andantino - attacca (7)\n01:24:12 String Quartet No. 5 in B-Flat Major, Op. 92: III. Moderato - Allegretto - Andante (7)\n01:34:22 String Quartet No. 7 in F-Sharp Minor, Op. 108: II. Lento - attacca (7)\n01:38:17 Symphony No. 13 in B-Flat Minor, Op. 113 \"Babi Yar\u201c: I. Babi Yar - Adagio (1)\n01:53:57 Sonata for Viola and Piano, Op. 147: I. Moderato (2)\n02:05:37 Sonata for Viola and Piano, Op. 147: III. Adagio (2)\n02:22:40 String Quartet No. 3, Op. 73: I. Allegretto (6)\n02:29:22 Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): I. Largo (3)\n02:34:09 Chamber Symphony in C minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): II. Allegro molto (3)\n02:37:13 Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): IV. Largo (3)\n02:42:47 Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): V. Largo (3)\n02:46:41 Hamlet - Suite from the Film, Op. 116 A: II. Ball at the Palace (4)\n02:49:45 Quartet No. 11 in F Minor, Op. 122 (5)\n03:06:47 String Quartet No. 2 in A Major, Op. 68: II. Recitative and Romance: Adagio (1)\n03:17:10 String Quartet No. 2 in A Major, Op. 68: III. Waltz: Allegro (1)\n03:22:36 String Quartet No. 4, Op. 83: III. Allegretto (4)\n\n\n\ud835\udc0f\ud835\udc1e\ud835\udc2b\ud835\udc1f\ud835\udc28\ud835\udc2b\ud835\udc26\ud835\udc1e\ud835\udc2b\ud835\udc2c\n----------------------\n(1) Georgian State String Quartet\n(2) Igor Uryash\n(3) Konzerthaus Kammerorchester Berlin\n(4) Lithuanian Chamber Orchestra\n(5) New Philharmonic Quartet\n(6) Novosibirsk Chamber Orchestra\n(7) Saint Petersburg String Quartet\n \n\n\nPlease follow and subscribe to our channel. We appreciate your support.\n\n\ud83d\udd14 Click the bell to stay updated on the great classical songs \n\n\ud83d\udce7 info@cugate.com\n\ud83c\udf10 www.cugate.com\n\n\ud83c\udfa7 Essential Classics is dedicated to sharing and promoting classical music. We strive to present the best classical music for you! Our hope is to make your days more beautiful and peaceful with classical music. \n\nAll audio and visual content on Essential Classics is created by Cugate AG a well-known music company. We have all legal rights of the audiovisual materials used in this video and our music is protected by copyright laws. If you have any questions regarding copyright or cooperation, please contact us.\n\n#classicalmusic", - "channel_id": "UCohcCsF4Eon2BWj3_QD__YQ", - "channel_url": "https://www.youtube.com/channel/UCohcCsF4Eon2BWj3_QD__YQ", - "duration": 12439, - "view_count": 28993, - "average_rating": null, - "age_limit": 0, - "webpage_url": "https://www.youtube.com/watch?v=vKTVLpmvznI", - "categories": [ - "Music" - ], - "tags": [ - "Dmitri Shostakovich", - "shostakovich", - "classical", - "shostakovich playlist", - "classical music", - "gadfly suite" - ], - "playable_in_embed": true, - "live_status": "not_live", - "media_type": null, - "release_timestamp": null, - "_format_sort_fields": [ - "quality", - "res", - "fps", - "hdr:12", - "source", - "vcodec", - "channels", - "acodec", - "lang", - "proto" - ], - "automatic_captions": {}, - "subtitles": {}, - "comment_count": 40, - "chapters": [ - { - "start_time": 0.0, - "title": "Prelude No. 17, Op. 34 (6)", - "end_time": 134.0 - }, - { - "start_time": 134.0, - "title": "Piano Concerto No. 1, Op. 35: II. Lento (6)", - "end_time": 565.0 - }, - { - "start_time": 565.0, - "title": "The Gadfly Suite, Op. 97a: II. Contredanse (4)", - "end_time": 755.0 - }, - { - "start_time": 755.0, - "title": "Hamlet - Suite from the Film, Op. 116 A: IV. In the Garden (4)", - "end_time": 915.0 - }, - { - "start_time": 915.0, - "title": "The Gadfly Suite, Op. 97a: V. Barrel Organ Waltz (4)", - "end_time": 1047.0 - }, - { - "start_time": 1047.0, - "title": "The Gadfly Suite, Op. 97a: IV. Interlude (4)", - "end_time": 1244.0 - }, - { - "start_time": 1244.0, - "title": "String Quartet No. 4, Op. 83: IV. Allegretto (4)", - "end_time": 1773.0 - }, - { - "start_time": 1773.0, - "title": "The Gadfly Suite, Op. 97a: VI. Galop (4)", - "end_time": 1882.0 - }, - { - "start_time": 1882.0, - "title": "The Gadfly Suite, Op. 97a: VII. Introduction (4)", - "end_time": 2291.0 - }, - { - "start_time": 2291.0, - "title": "The Gadfly Suite, Op. 97a: VIII. Romance (4)", - "end_time": 2728.0 - }, - { - "start_time": 2728.0, - "title": "The Gadfly Suite, Op. 97a: IX. Intermezzo (4)", - "end_time": 3078.0 - }, - { - "start_time": 3078.0, - "title": "The Gadfly Suite, Op. 97a: X. Nocturne (4)", - "end_time": 3323.0 - }, - { - "start_time": 3323.0, - "title": "The Gadfly Suite, Op. 97a: XI. Scene (4)", - "end_time": 3498.0 - }, - { - "start_time": 3498.0, - "title": "Hamlet - Suite from the Film, Op. 116 A: VII. Ophelia (4)", - "end_time": 3767.0 - }, - { - "start_time": 3767.0, - "title": "String Quartet No. 4, Op. 83: I. Allegretto (4)", - "end_time": 4074.0 - }, - { - "start_time": 4074.0, - "title": "String Quartet No. 4, Op. 83: II. Andantino (4)", - "end_time": 4555.0 - }, - { - "start_time": 4555.0, - "title": "String Quartet No. 5 in B-Flat Major, Op. 92: II. Andante - Andantino - attacca (7)", - "end_time": 5052.0 - }, - { - "start_time": 5052.0, - "title": "String Quartet No. 5 in B-Flat Major, Op. 92: III. Moderato - Allegretto - Andante (7)", - "end_time": 5662.0 - }, - { - "start_time": 5662.0, - "title": "String Quartet No. 7 in F-Sharp Minor, Op. 108: II. Lento - attacca (7)", - "end_time": 5897.0 - }, - { - "start_time": 5897.0, - "title": "Symphony No. 13 in B-Flat Minor, Op. 113 \"Babi Yar\u201c: I. Babi Yar - Adagio (1)", - "end_time": 6837.0 - }, - { - "start_time": 6837.0, - "title": "Sonata for Viola and Piano, Op. 147: I. Moderato (2)", - "end_time": 7537.0 - }, - { - "start_time": 7537.0, - "title": "Sonata for Viola and Piano, Op. 147: III. Adagio (2)", - "end_time": 8560.0 - }, - { - "start_time": 8560.0, - "title": "String Quartet No. 3, Op. 73: I. Allegretto (6)", - "end_time": 8962.0 - }, - { - "start_time": 8962.0, - "title": "Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): I. Largo (3)", - "end_time": 9249.0 - }, - { - "start_time": 9249.0, - "title": "Chamber Symphony in C minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): II. Allegro molto (3)", - "end_time": 9433.0 - }, - { - "start_time": 9433.0, - "title": "Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): IV. Largo (3)", - "end_time": 9767.0 - }, - { - "start_time": 9767.0, - "title": "Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): V. Largo (3)", - "end_time": 10001.0 - }, - { - "start_time": 10001.0, - "title": "Hamlet - Suite from the Film, Op. 116 A: II. Ball at the Palace (4)", - "end_time": 10185.0 - }, - { - "start_time": 10185.0, - "title": "Quartet No. 11 in F Minor, Op. 122 (5)", - "end_time": 11207.0 - }, - { - "start_time": 11207.0, - "title": "String Quartet No. 2 in A Major, Op. 68: II. Recitative and Romance: Adagio (1)", - "end_time": 11830.0 - }, - { - "start_time": 11830.0, - "title": "String Quartet No. 2 in A Major, Op. 68: III. Waltz: Allegro (1)", - "end_time": 12156.0 - }, - { - "start_time": 12156.0, - "title": "String Quartet No. 4, Op. 83: III. Allegretto (4)", - "end_time": 12439 - } - ], - "heatmap": null, - "like_count": 732, - "channel": "Essential Classics", - "channel_follower_count": 252000, - "uploader": "Essential Classics", - "uploader_id": "@EssentialClassics", - "uploader_url": "https://www.youtube.com/@EssentialClassics", - "upload_date": "20250324", - "timestamp": 1742831917, - "availability": "public", - "original_url": "https://www.youtube.com/watch?v=vKTVLpmvznI", - "webpage_url_basename": "watch", - "webpage_url_domain": "youtube.com", - "extractor": "youtube+GetPOT", - "extractor_key": "Youtube", - "playlist": null, - "playlist_index": null, - "display_id": "vKTVLpmvznI", - "fulltitle": "Best of Dmitri Shostakovich - Essential Classical Music", - "duration_string": "3:27:19", - "release_year": null, - "is_live": false, - "was_live": false, - "requested_subtitles": null, - "_has_drm": null, - "epoch": 1743507631, - "asr": 44100, - "filesize": 401472565, - "format_id": "18", - "format_note": "360p, WEB", - "source_preference": -1, - "fps": 25, - "audio_channels": 2, - "height": 360, - "quality": 6.0, - "has_drm": false, - "tbr": 258.192, - "filesize_approx": 401471228, - "url": "https://rr5---sn-4g5edndz.googlevideo.com/videoplayback?expire=1743529225&ei=qdDrZ-LsKoH54-EP2PCkqQg&ip=79.137.202.19&id=o-AECkHm_LNzZwxiB7DWgZ8xD1oTygyyn0lqkf8gis7zLD&itag=18&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&met=1743507625%2C&mh=k5&mm=31%2C26&mn=sn-4g5edndz%2Csn-h0jeener&ms=au%2Conr&mv=m&mvi=5&pl=23&rms=au%2Cau&initcwndbps=1266250&bui=AccgBcP5u60QMh-eqPBu3EhQFDx5iW0GHP48OVcWCQvImxZdHHOPrnwHlxiCQ6BcyiAEoZ1dCPexFX03&spc=_S3wKlc5vLQyAwz48PlBCy5ej7_4zYJhl12k4Oug_Nhji995VJmceRP6jKVKCcneIEB5Lkl94A&vprv=1&svpuc=1&mime=video%2Fmp4&ns=TQbcu0FMoR96nxJoch6pqrgQ&rqh=1&gir=yes&clen=401472565&ratebypass=yes&dur=12439.463&lmt=1743385980965512&mt=1743507195&fvip=3&fexp=51355912%2C51435733&c=WEB&sefc=1&txp=4438534&n=MZTYGnga5TOGoA&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&lsparams=met%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Crms%2Cinitcwndbps&lsig=AFVRHeAwRAIgbq4n7-4EohQ39k0IvhhaAtlVFSfOumJpwaKa5fqP2WICIBDx3yAKFs152Tw-HV5REwBGHOYPocxoiHTlp6UdOBXA&sig=AJfQdSswRgIhALPnRNBR401MCvj3BHaAP7tz1666bRiXjTU8XnhCM5EhAiEAt9rzbdV9bT1mOKd57NA9xdKdeQGWlyqJoAdzUsFctWg%3D", - "width": 640, - "language": null, - "language_preference": -1, - "preference": null, - "ext": "mp4", - "vcodec": "avc1.42001E", - "acodec": "mp4a.40.2", - "dynamic_range": "SDR", - "downloader_options": { - "http_chunk_size": 10485760 - }, - "protocol": "https", - "video_ext": "mp4", - "audio_ext": "none", - "vbr": null, - "abr": null, - "resolution": "640x360", - "aspect_ratio": 1.78, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "18 - 640x360 (360p, WEB)" -} \ No newline at end of file diff --git a/ytdlp-ops-auth/latest.json b/ytdlp-ops-auth/latest.json deleted file mode 100644 index 4a87bfb..0000000 --- a/ytdlp-ops-auth/latest.json +++ /dev/null @@ -1,1474 +0,0 @@ -{ - "id": "vKTVLpmvznI", - "title": "Best of Dmitri Shostakovich - Essential Classical Music", - "formats": [ - { - "format_id": "sb3", - "format_note": "storyboard", - "ext": "mhtml", - "protocol": "mhtml", - "acodec": "none", - "vcodec": "none", - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCNceDOVve20wuvVEw7USJxQaljmA", - "width": 48, - "height": 27, - "fps": 0.00803923144947343, - "rows": 10, - "columns": 10, - "fragments": [ - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCNceDOVve20wuvVEw7USJxQaljmA", - "duration": 12439.0 - } - ], - "audio_ext": "none", - "video_ext": "none", - "vbr": 0, - "abr": 0, - "tbr": null, - "resolution": "48x27", - "aspect_ratio": 1.78, - "filesize_approx": null, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "sb3 - 48x27 (storyboard)" - }, - { - "format_id": "sb2", - "format_note": "storyboard", - "ext": "mhtml", - "protocol": "mhtml", - "acodec": "none", - "vcodec": "none", - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "width": 80, - "height": 45, - "fps": 0.10008843154594421, - "rows": 10, - "columns": 10, - "fragments": [ - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M0.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M1.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M2.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M3.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M4.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M5.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M6.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M7.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M8.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M9.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M10.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M11.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 999.1164658634539 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L1/M12.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAEYJKXmPvCqppfugFJ5yrU35edTg", - "duration": 449.6024096385536 - } - ], - "audio_ext": "none", - "video_ext": "none", - "vbr": 0, - "abr": 0, - "tbr": null, - "resolution": "80x45", - "aspect_ratio": 1.78, - "filesize_approx": null, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "sb2 - 80x45 (storyboard)" - }, - { - "format_id": "sb1", - "format_note": "storyboard", - "ext": "mhtml", - "protocol": "mhtml", - "acodec": "none", - "vcodec": "none", - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "width": 160, - "height": 90, - "fps": 0.10008843154594421, - "rows": 5, - "columns": 5, - "fragments": [ - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M0.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M1.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M2.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M3.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M4.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M5.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M6.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M7.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M8.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M9.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M10.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M11.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M12.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M13.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M14.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M15.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M16.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M17.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M18.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M19.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M20.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M21.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M22.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M23.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M24.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M25.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M26.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M27.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M28.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M29.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M30.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M31.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M32.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M33.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M34.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M35.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M36.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M37.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M38.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M39.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M40.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M41.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M42.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M43.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M44.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M45.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M46.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M47.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M48.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 249.77911646586347 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L2/M49.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLCqwWWb7Xq_jktYDAyQOcmKDQFLkQ", - "duration": 199.82329317269068 - } - ], - "audio_ext": "none", - "video_ext": "none", - "vbr": 0, - "abr": 0, - "tbr": null, - "resolution": "160x90", - "aspect_ratio": 1.78, - "filesize_approx": null, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "sb1 - 160x90 (storyboard)" - }, - { - "format_id": "sb0", - "format_note": "storyboard", - "ext": "mhtml", - "protocol": "mhtml", - "acodec": "none", - "vcodec": "none", - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "width": 320, - "height": 180, - "fps": 0.10008843154594421, - "rows": 3, - "columns": 3, - "fragments": [ - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M0.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M1.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M2.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M3.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M4.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M5.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M6.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M7.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M8.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M9.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M10.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M11.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M12.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M13.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M14.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M15.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M16.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M17.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M18.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M19.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M20.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M21.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M22.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M23.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M24.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M25.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M26.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M27.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M28.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M29.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M30.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M31.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M32.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M33.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M34.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M35.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M36.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M37.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M38.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M39.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M40.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M41.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M42.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M43.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M44.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M45.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M46.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M47.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M48.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M49.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M50.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M51.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M52.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M53.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M54.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M55.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M56.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M57.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M58.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M59.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M60.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M61.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M62.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M63.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M64.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M65.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M66.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M67.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M68.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M69.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M70.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M71.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M72.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M73.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M74.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M75.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M76.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M77.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M78.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M79.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M80.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M81.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M82.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M83.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M84.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M85.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M86.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M87.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M88.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M89.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M90.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M91.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M92.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M93.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M94.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M95.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M96.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M97.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M98.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M99.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M100.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M101.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M102.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M103.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M104.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M105.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M106.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M107.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M108.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M109.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M110.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M111.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M112.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M113.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M114.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M115.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M116.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M117.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M118.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M119.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M120.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M121.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M122.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M123.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M124.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M125.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M126.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M127.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M128.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M129.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M130.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M131.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M132.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M133.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M134.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M135.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M136.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M137.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 89.92048192771084 - }, - { - "url": "https://i.ytimg.com/sb/vKTVLpmvznI/storyboard3_L3/M138.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgix64W_Bg==&sigh=rs$AOn4CLAheiEOU2YJr-6mgJtVTX9ATNC-Ow", - "duration": 29.973493975903693 - } - ], - "audio_ext": "none", - "video_ext": "none", - "vbr": 0, - "abr": 0, - "tbr": null, - "resolution": "320x180", - "aspect_ratio": 1.78, - "filesize_approx": null, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "sb0 - 320x180 (storyboard)" - }, - { - "asr": 44100, - "filesize": 401472565, - "format_id": "18", - "format_note": "360p, WEB", - "source_preference": -1, - "fps": 25, - "audio_channels": 2, - "height": 360, - "quality": 6.0, - "has_drm": false, - "tbr": 258.192, - "filesize_approx": 401471228, - "url": "https://rr5---sn-4g5edndz.googlevideo.com/videoplayback?expire=1743529225&ei=qdDrZ-LsKoH54-EP2PCkqQg&ip=79.137.202.19&id=o-AECkHm_LNzZwxiB7DWgZ8xD1oTygyyn0lqkf8gis7zLD&itag=18&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&met=1743507625%2C&mh=k5&mm=31%2C26&mn=sn-4g5edndz%2Csn-h0jeener&ms=au%2Conr&mv=m&mvi=5&pl=23&rms=au%2Cau&initcwndbps=1266250&bui=AccgBcP5u60QMh-eqPBu3EhQFDx5iW0GHP48OVcWCQvImxZdHHOPrnwHlxiCQ6BcyiAEoZ1dCPexFX03&spc=_S3wKlc5vLQyAwz48PlBCy5ej7_4zYJhl12k4Oug_Nhji995VJmceRP6jKVKCcneIEB5Lkl94A&vprv=1&svpuc=1&mime=video%2Fmp4&ns=TQbcu0FMoR96nxJoch6pqrgQ&rqh=1&gir=yes&clen=401472565&ratebypass=yes&dur=12439.463&lmt=1743385980965512&mt=1743507195&fvip=3&fexp=51355912%2C51435733&c=WEB&sefc=1&txp=4438534&n=MZTYGnga5TOGoA&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&lsparams=met%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Crms%2Cinitcwndbps&lsig=AFVRHeAwRAIgbq4n7-4EohQ39k0IvhhaAtlVFSfOumJpwaKa5fqP2WICIBDx3yAKFs152Tw-HV5REwBGHOYPocxoiHTlp6UdOBXA&sig=AJfQdSswRgIhALPnRNBR401MCvj3BHaAP7tz1666bRiXjTU8XnhCM5EhAiEAt9rzbdV9bT1mOKd57NA9xdKdeQGWlyqJoAdzUsFctWg%3D", - "width": 640, - "language": null, - "language_preference": -1, - "preference": null, - "ext": "mp4", - "vcodec": "avc1.42001E", - "acodec": "mp4a.40.2", - "dynamic_range": "SDR", - "downloader_options": { - "http_chunk_size": 10485760 - }, - "protocol": "https", - "video_ext": "mp4", - "audio_ext": "none", - "vbr": null, - "abr": null, - "resolution": "640x360", - "aspect_ratio": 1.78, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "18 - 640x360 (360p, WEB)" - } - ], - "thumbnails": [ - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/3.jpg", - "preference": -37, - "id": "0" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/3.webp", - "preference": -36, - "id": "1" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/2.jpg", - "preference": -35, - "id": "2" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/2.webp", - "preference": -34, - "id": "3" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/1.jpg", - "preference": -33, - "id": "4" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/1.webp", - "preference": -32, - "id": "5" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/mq3.jpg", - "preference": -31, - "id": "6" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/mq3.webp", - "preference": -30, - "id": "7" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/mq2.jpg", - "preference": -29, - "id": "8" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/mq2.webp", - "preference": -28, - "id": "9" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/mq1.jpg", - "preference": -27, - "id": "10" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/mq1.webp", - "preference": -26, - "id": "11" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hq3.jpg", - "preference": -25, - "id": "12" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/hq3.webp", - "preference": -24, - "id": "13" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hq2.jpg", - "preference": -23, - "id": "14" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/hq2.webp", - "preference": -22, - "id": "15" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hq1.jpg", - "preference": -21, - "id": "16" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/hq1.webp", - "preference": -20, - "id": "17" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/sd3.jpg", - "preference": -19, - "id": "18" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/sd3.webp", - "preference": -18, - "id": "19" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/sd2.jpg", - "preference": -17, - "id": "20" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/sd2.webp", - "preference": -16, - "id": "21" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/sd1.jpg", - "preference": -15, - "id": "22" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/sd1.webp", - "preference": -14, - "id": "23" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/default.jpg", - "preference": -13, - "id": "24" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/default.webp", - "preference": -12, - "id": "25" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/mqdefault.jpg", - "preference": -11, - "id": "26" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/mqdefault.webp", - "preference": -10, - "id": "27" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/0.jpg", - "preference": -9, - "id": "28" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/0.webp", - "preference": -8, - "id": "29" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hqdefault.jpg", - "preference": -7, - "id": "30" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLCWGGqXfZOyDXVDblw-T6EV_UmHOw", - "height": 94, - "width": 168, - "preference": -7, - "id": "31", - "resolution": "168x94" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLCc2LZi4OgeI7BJQMT5IKNWi6B9VQ", - "height": 110, - "width": 196, - "preference": -7, - "id": "32", - "resolution": "196x110" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAvvAjHqsZKN97OayOaKmjUsuYfuA", - "height": 138, - "width": 246, - "preference": -7, - "id": "33", - "resolution": "246x138" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLA3kWM8Xrcnk0DKdMWwnyD1b3Kn7g", - "height": 188, - "width": 336, - "preference": -7, - "id": "34", - "resolution": "336x188" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/hqdefault.webp", - "preference": -6, - "id": "35" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/sddefault.jpg", - "preference": -5, - "id": "36" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/sddefault.webp", - "preference": -4, - "id": "37" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/hq720.jpg", - "preference": -3, - "id": "38" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/hq720.webp", - "preference": -2, - "id": "39" - }, - { - "url": "https://i.ytimg.com/vi/vKTVLpmvznI/maxresdefault.jpg", - "height": 1080, - "width": 1920, - "preference": -1, - "id": "40", - "resolution": "1920x1080" - }, - { - "url": "https://i.ytimg.com/vi_webp/vKTVLpmvznI/maxresdefault.webp", - "preference": 0, - "id": "41" - } - ], - "thumbnail": "https://i.ytimg.com/vi/vKTVLpmvznI/maxresdefault.jpg", - "description": "Shostakovich\u2019s music is a testament to resilience, defiance, and profound emotion. His compositions, marked by striking contrasts and deep intensity, reflect both the struggles of his time and the brilliance of his creative mind. From hauntingly beautiful adagios to electrifying symphonic movements, his work carries a unique voice that speaks of both sorrow and triumph.\n\nThis collection brings together some of his most powerful and evocative works, showcasing his mastery of orchestration, his sharp wit, and his deep understanding of human emotion. Whether it\u2019s the raw intensity of his symphonies, the elegance of his chamber music, or the unsettling beauty of his piano pieces, every note is charged with meaning.\n\n\ud83d\udfe2 Follow us and our playlists on Spotify:\nhttps://open.spotify.com/user/cugate-music?si=2d9adbc210b64a2b\n\n\ud83d\udc49 Listen to classical music on other streaming platforms. The links are below: \n\n\ud83d\udccc \ud835\udc13\ud835\udc21\ud835\udc1e \ud835\udfcf\ud835\udfce\ud835\udfce \ud835\udc01\ud835\udc1e\ud835\udc2c\ud835\udc2d \ud835\udc0e\ud835\udc1f \ud835\udc02\ud835\udc25\ud835\udc1a\ud835\udc2c\ud835\udc2c\ud835\udc22\ud835\udc1c: https://lnk.to/Various-Artists-The-100-Best-Of-Classic-Volume-2\n\ud83d\udccc \ud835\udc01\ud835\udc1e\ud835\udc2c\ud835\udc2d \ud835\udc28\ud835\udc1f \ud835\udc03\ud835\udc1e\ud835\udc1b\ud835\udc2e\ud835\udc2c\ud835\udc2c\ud835\udc32: https://lnk.to/Various-Artists-Best-of-Debussy\n\ud83d\udccc \ud835\udc01\ud835\udc1e\ud835\udc2c\ud835\udc2d \ud835\udc28\ud835\udc1f \ud835\udc07\ud835\udc1a\ud835\udc27\ud835\udc1d\ud835\udc1e\ud835\udc25: https://lnk.to/Various-Artists-Best-of-Handel\n\n\n\ud835\udc0f\ud835\udc0b\ud835\udc00\ud835\udc18\ud835\udc0b\ud835\udc08\ud835\udc12\ud835\udc13\n--------------------\n00:00:00 Prelude No. 17, Op. 34 (6)\n00:02:14 Piano Concerto No. 1, Op. 35: II. Lento (6)\n00:09:25 The Gadfly Suite, Op. 97a: II. Contredanse (4)\n00:12:35 Hamlet - Suite from the Film, Op. 116 A: IV. In the Garden (4)\n00:15:15 The Gadfly Suite, Op. 97a: V. Barrel Organ Waltz (4)\n00:17:27 The Gadfly Suite, Op. 97a: IV. Interlude (4)\n00:20:44 String Quartet No. 4, Op. 83: IV. Allegretto (4)\n00:29:33 The Gadfly Suite, Op. 97a: VI. Galop (4)\n00:31:22 The Gadfly Suite, Op. 97a: VII. Introduction (4)\n00:38:11 The Gadfly Suite, Op. 97a: VIII. Romance (4)\n00:45:28 The Gadfly Suite, Op. 97a: IX. Intermezzo (4)\n00:51:18 The Gadfly Suite, Op. 97a: X. Nocturne (4)\n00:55:23 The Gadfly Suite, Op. 97a: XI. Scene (4)\n00:58:18 Hamlet - Suite from the Film, Op. 116 A: VII. Ophelia (4)\n01:02:47 String Quartet No. 4, Op. 83: I. Allegretto (4)\n01:07:54 String Quartet No. 4, Op. 83: II. Andantino (4)\n01:15:55 String Quartet No. 5 in B-Flat Major, Op. 92: II. Andante - Andantino - attacca (7)\n01:24:12 String Quartet No. 5 in B-Flat Major, Op. 92: III. Moderato - Allegretto - Andante (7)\n01:34:22 String Quartet No. 7 in F-Sharp Minor, Op. 108: II. Lento - attacca (7)\n01:38:17 Symphony No. 13 in B-Flat Minor, Op. 113 \"Babi Yar\u201c: I. Babi Yar - Adagio (1)\n01:53:57 Sonata for Viola and Piano, Op. 147: I. Moderato (2)\n02:05:37 Sonata for Viola and Piano, Op. 147: III. Adagio (2)\n02:22:40 String Quartet No. 3, Op. 73: I. Allegretto (6)\n02:29:22 Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): I. Largo (3)\n02:34:09 Chamber Symphony in C minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): II. Allegro molto (3)\n02:37:13 Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): IV. Largo (3)\n02:42:47 Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): V. Largo (3)\n02:46:41 Hamlet - Suite from the Film, Op. 116 A: II. Ball at the Palace (4)\n02:49:45 Quartet No. 11 in F Minor, Op. 122 (5)\n03:06:47 String Quartet No. 2 in A Major, Op. 68: II. Recitative and Romance: Adagio (1)\n03:17:10 String Quartet No. 2 in A Major, Op. 68: III. Waltz: Allegro (1)\n03:22:36 String Quartet No. 4, Op. 83: III. Allegretto (4)\n\n\n\ud835\udc0f\ud835\udc1e\ud835\udc2b\ud835\udc1f\ud835\udc28\ud835\udc2b\ud835\udc26\ud835\udc1e\ud835\udc2b\ud835\udc2c\n----------------------\n(1) Georgian State String Quartet\n(2) Igor Uryash\n(3) Konzerthaus Kammerorchester Berlin\n(4) Lithuanian Chamber Orchestra\n(5) New Philharmonic Quartet\n(6) Novosibirsk Chamber Orchestra\n(7) Saint Petersburg String Quartet\n \n\n\nPlease follow and subscribe to our channel. We appreciate your support.\n\n\ud83d\udd14 Click the bell to stay updated on the great classical songs \n\n\ud83d\udce7 info@cugate.com\n\ud83c\udf10 www.cugate.com\n\n\ud83c\udfa7 Essential Classics is dedicated to sharing and promoting classical music. We strive to present the best classical music for you! Our hope is to make your days more beautiful and peaceful with classical music. \n\nAll audio and visual content on Essential Classics is created by Cugate AG a well-known music company. We have all legal rights of the audiovisual materials used in this video and our music is protected by copyright laws. If you have any questions regarding copyright or cooperation, please contact us.\n\n#classicalmusic", - "channel_id": "UCohcCsF4Eon2BWj3_QD__YQ", - "channel_url": "https://www.youtube.com/channel/UCohcCsF4Eon2BWj3_QD__YQ", - "duration": 12439, - "view_count": 28993, - "average_rating": null, - "age_limit": 0, - "webpage_url": "https://www.youtube.com/watch?v=vKTVLpmvznI", - "categories": [ - "Music" - ], - "tags": [ - "Dmitri Shostakovich", - "shostakovich", - "classical", - "shostakovich playlist", - "classical music", - "gadfly suite" - ], - "playable_in_embed": true, - "live_status": "not_live", - "media_type": null, - "release_timestamp": null, - "_format_sort_fields": [ - "quality", - "res", - "fps", - "hdr:12", - "source", - "vcodec", - "channels", - "acodec", - "lang", - "proto" - ], - "automatic_captions": {}, - "subtitles": {}, - "comment_count": 40, - "chapters": [ - { - "start_time": 0.0, - "title": "Prelude No. 17, Op. 34 (6)", - "end_time": 134.0 - }, - { - "start_time": 134.0, - "title": "Piano Concerto No. 1, Op. 35: II. Lento (6)", - "end_time": 565.0 - }, - { - "start_time": 565.0, - "title": "The Gadfly Suite, Op. 97a: II. Contredanse (4)", - "end_time": 755.0 - }, - { - "start_time": 755.0, - "title": "Hamlet - Suite from the Film, Op. 116 A: IV. In the Garden (4)", - "end_time": 915.0 - }, - { - "start_time": 915.0, - "title": "The Gadfly Suite, Op. 97a: V. Barrel Organ Waltz (4)", - "end_time": 1047.0 - }, - { - "start_time": 1047.0, - "title": "The Gadfly Suite, Op. 97a: IV. Interlude (4)", - "end_time": 1244.0 - }, - { - "start_time": 1244.0, - "title": "String Quartet No. 4, Op. 83: IV. Allegretto (4)", - "end_time": 1773.0 - }, - { - "start_time": 1773.0, - "title": "The Gadfly Suite, Op. 97a: VI. Galop (4)", - "end_time": 1882.0 - }, - { - "start_time": 1882.0, - "title": "The Gadfly Suite, Op. 97a: VII. Introduction (4)", - "end_time": 2291.0 - }, - { - "start_time": 2291.0, - "title": "The Gadfly Suite, Op. 97a: VIII. Romance (4)", - "end_time": 2728.0 - }, - { - "start_time": 2728.0, - "title": "The Gadfly Suite, Op. 97a: IX. Intermezzo (4)", - "end_time": 3078.0 - }, - { - "start_time": 3078.0, - "title": "The Gadfly Suite, Op. 97a: X. Nocturne (4)", - "end_time": 3323.0 - }, - { - "start_time": 3323.0, - "title": "The Gadfly Suite, Op. 97a: XI. Scene (4)", - "end_time": 3498.0 - }, - { - "start_time": 3498.0, - "title": "Hamlet - Suite from the Film, Op. 116 A: VII. Ophelia (4)", - "end_time": 3767.0 - }, - { - "start_time": 3767.0, - "title": "String Quartet No. 4, Op. 83: I. Allegretto (4)", - "end_time": 4074.0 - }, - { - "start_time": 4074.0, - "title": "String Quartet No. 4, Op. 83: II. Andantino (4)", - "end_time": 4555.0 - }, - { - "start_time": 4555.0, - "title": "String Quartet No. 5 in B-Flat Major, Op. 92: II. Andante - Andantino - attacca (7)", - "end_time": 5052.0 - }, - { - "start_time": 5052.0, - "title": "String Quartet No. 5 in B-Flat Major, Op. 92: III. Moderato - Allegretto - Andante (7)", - "end_time": 5662.0 - }, - { - "start_time": 5662.0, - "title": "String Quartet No. 7 in F-Sharp Minor, Op. 108: II. Lento - attacca (7)", - "end_time": 5897.0 - }, - { - "start_time": 5897.0, - "title": "Symphony No. 13 in B-Flat Minor, Op. 113 \"Babi Yar\u201c: I. Babi Yar - Adagio (1)", - "end_time": 6837.0 - }, - { - "start_time": 6837.0, - "title": "Sonata for Viola and Piano, Op. 147: I. Moderato (2)", - "end_time": 7537.0 - }, - { - "start_time": 7537.0, - "title": "Sonata for Viola and Piano, Op. 147: III. Adagio (2)", - "end_time": 8560.0 - }, - { - "start_time": 8560.0, - "title": "String Quartet No. 3, Op. 73: I. Allegretto (6)", - "end_time": 8962.0 - }, - { - "start_time": 8962.0, - "title": "Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): I. Largo (3)", - "end_time": 9249.0 - }, - { - "start_time": 9249.0, - "title": "Chamber Symphony in C minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): II. Allegro molto (3)", - "end_time": 9433.0 - }, - { - "start_time": 9433.0, - "title": "Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): IV. Largo (3)", - "end_time": 9767.0 - }, - { - "start_time": 9767.0, - "title": "Chamber Symphony in C Minor, Op. 110 a (arr. by Barshai from String Quartet No. 8 ): V. Largo (3)", - "end_time": 10001.0 - }, - { - "start_time": 10001.0, - "title": "Hamlet - Suite from the Film, Op. 116 A: II. Ball at the Palace (4)", - "end_time": 10185.0 - }, - { - "start_time": 10185.0, - "title": "Quartet No. 11 in F Minor, Op. 122 (5)", - "end_time": 11207.0 - }, - { - "start_time": 11207.0, - "title": "String Quartet No. 2 in A Major, Op. 68: II. Recitative and Romance: Adagio (1)", - "end_time": 11830.0 - }, - { - "start_time": 11830.0, - "title": "String Quartet No. 2 in A Major, Op. 68: III. Waltz: Allegro (1)", - "end_time": 12156.0 - }, - { - "start_time": 12156.0, - "title": "String Quartet No. 4, Op. 83: III. Allegretto (4)", - "end_time": 12439 - } - ], - "heatmap": null, - "like_count": 732, - "channel": "Essential Classics", - "channel_follower_count": 252000, - "uploader": "Essential Classics", - "uploader_id": "@EssentialClassics", - "uploader_url": "https://www.youtube.com/@EssentialClassics", - "upload_date": "20250324", - "timestamp": 1742831917, - "availability": "public", - "original_url": "https://www.youtube.com/watch?v=vKTVLpmvznI", - "webpage_url_basename": "watch", - "webpage_url_domain": "youtube.com", - "extractor": "youtube+GetPOT", - "extractor_key": "Youtube", - "playlist": null, - "playlist_index": null, - "display_id": "vKTVLpmvznI", - "fulltitle": "Best of Dmitri Shostakovich - Essential Classical Music", - "duration_string": "3:27:19", - "release_year": null, - "is_live": false, - "was_live": false, - "requested_subtitles": null, - "_has_drm": null, - "epoch": 1743507631, - "asr": 44100, - "filesize": 401472565, - "format_id": "18", - "format_note": "360p, WEB", - "source_preference": -1, - "fps": 25, - "audio_channels": 2, - "height": 360, - "quality": 6.0, - "has_drm": false, - "tbr": 258.192, - "filesize_approx": 401471228, - "url": "https://rr5---sn-4g5edndz.googlevideo.com/videoplayback?expire=1743529225&ei=qdDrZ-LsKoH54-EP2PCkqQg&ip=79.137.202.19&id=o-AECkHm_LNzZwxiB7DWgZ8xD1oTygyyn0lqkf8gis7zLD&itag=18&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&met=1743507625%2C&mh=k5&mm=31%2C26&mn=sn-4g5edndz%2Csn-h0jeener&ms=au%2Conr&mv=m&mvi=5&pl=23&rms=au%2Cau&initcwndbps=1266250&bui=AccgBcP5u60QMh-eqPBu3EhQFDx5iW0GHP48OVcWCQvImxZdHHOPrnwHlxiCQ6BcyiAEoZ1dCPexFX03&spc=_S3wKlc5vLQyAwz48PlBCy5ej7_4zYJhl12k4Oug_Nhji995VJmceRP6jKVKCcneIEB5Lkl94A&vprv=1&svpuc=1&mime=video%2Fmp4&ns=TQbcu0FMoR96nxJoch6pqrgQ&rqh=1&gir=yes&clen=401472565&ratebypass=yes&dur=12439.463&lmt=1743385980965512&mt=1743507195&fvip=3&fexp=51355912%2C51435733&c=WEB&sefc=1&txp=4438534&n=MZTYGnga5TOGoA&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&lsparams=met%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Crms%2Cinitcwndbps&lsig=AFVRHeAwRAIgbq4n7-4EohQ39k0IvhhaAtlVFSfOumJpwaKa5fqP2WICIBDx3yAKFs152Tw-HV5REwBGHOYPocxoiHTlp6UdOBXA&sig=AJfQdSswRgIhALPnRNBR401MCvj3BHaAP7tz1666bRiXjTU8XnhCM5EhAiEAt9rzbdV9bT1mOKd57NA9xdKdeQGWlyqJoAdzUsFctWg%3D", - "width": 640, - "language": null, - "language_preference": -1, - "preference": null, - "ext": "mp4", - "vcodec": "avc1.42001E", - "acodec": "mp4a.40.2", - "dynamic_range": "SDR", - "downloader_options": { - "http_chunk_size": 10485760 - }, - "protocol": "https", - "video_ext": "mp4", - "audio_ext": "none", - "vbr": null, - "abr": null, - "resolution": "640x360", - "aspect_ratio": 1.78, - "http_headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.70 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-us,en;q=0.5", - "Sec-Fetch-Mode": "navigate" - }, - "format": "18 - 640x360 (360p, WEB)" -} \ No newline at end of file