# Path: pytube/extract.py
# def publish_date(watch_html: str):
# def recording_available(watch_html):
# def is_private(watch_html):
# def is_age_restricted(watch_html: str) -> bool:
# def playability_status(watch_html: str) -> (str, str):
# def video_id(url: str) -> str:
# def playlist_id(url: str) -> str:
# def channel_name(url: str) -> str:
# def video_info_url(video_id: str, watch_url: str) -> str:
# def video_info_url_age_restricted(video_id: str, embed_html: str) -> str:
# def _video_info_url(params: OrderedDict) -> str:
# def js_url(html: str) -> str:
# def mime_type_codec(mime_type_codec: str) -> Tuple[str, List[str]]:
# def get_ytplayer_js(html: str) -> Any:
# def get_ytplayer_config(html: str) -> Any:
# def get_ytcfg(html: str) -> str:
# def apply_signature(stream_manifest: Dict, vid_info: Dict, js: str) -> None:
# def apply_descrambler(stream_data: Dict) -> None:
# def initial_data(watch_html: str) -> str:
# def initial_player_response(watch_html: str) -> str:
# def metadata(initial_data) -> Optional[YouTubeMetadata]:
# 
# Path: pytube/request.py
# def _execute_request(
#     url,
#     method=None,
#     headers=None,
#     data=None,
#     timeout=socket._GLOBAL_DEFAULT_TIMEOUT
# ):
# def get(url, extra_headers=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
# def post(url, extra_headers=None, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
# def seq_stream(
#     url,
#     timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
#     max_retries=0
# ):
# def stream(
#     url,
#     timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
#     max_retries=0
# ):
# def filesize(url):
# def seq_filesize(url):
# def head(url):
# 
# Path: pytube/helpers.py
# def safe_filename(s: str, max_length: int = 255) -> str:
#     """Sanitize a string making it safe to use as a filename.
# 
#     This function was based off the limitations outlined here:
#     https://en.wikipedia.org/wiki/Filename.
# 
#     :param str s:
#         A string to make safe for use as a file name.
#     :param int max_length:
#         The maximum filename character length.
#     :rtype: str
#     :returns:
#         A sanitized string.
#     """
#     # Characters in range 0-31 (0x00-0x1F) are not allowed in ntfs filenames.
#     ntfs_characters = [chr(i) for i in range(0, 31)]
#     characters = [
#         r'"',
#         r"\#",
#         r"\$",
#         r"\%",
#         r"'",
#         r"\*",
#         r"\,",
#         r"\.",
#         r"\/",
#         r"\:",
#         r'"',
#         r"\;",
#         r"\<",
#         r"\>",
#         r"\?",
#         r"\\",
#         r"\^",
#         r"\|",
#         r"\~",
#         r"\\\\",
#     ]
#     pattern = "|".join(ntfs_characters + characters)
#     regex = re.compile(pattern, re.UNICODE)
#     filename = regex.sub("", s)
#     return filename[:max_length].rsplit(" ", 0)[0]
# 
# def target_directory(output_path: Optional[str] = None) -> str:
#     """
#     Function for determining target directory of a download.
#     Returns an absolute path (if relative one given) or the current
#     path (if none given). Makes directory if it does not exist.
# 
#     :type output_path: str
#         :rtype: str
#     :returns:
#         An absolute directory path as a string.
#     """
#     if output_path:
#         if not os.path.isabs(output_path):
#             output_path = os.path.join(os.getcwd(), output_path)
#     else:
#         output_path = os.getcwd()
#     os.makedirs(output_path, exist_ok=True)
#     return output_path
# 
# Path: pytube/itags.py
# def get_format_profile(itag: int) -> Dict:
#     """Get additional format information for a given itag.
# 
#     :param str itag:
#         YouTube format identifier code.
#     """
#     itag = int(itag)
#     if itag in ITAGS:
#         res, bitrate = ITAGS[itag]
#     else:
#         res, bitrate = None, None
#     return {
#         "resolution": res,
#         "abr": bitrate,
#         "is_live": itag in LIVE,
#         "is_3d": itag in _3D,
#         "is_hdr": itag in HDR,
#         "is_dash": (
#             itag in DASH_AUDIO
#             or itag in DASH_VIDEO
#         ),
#     }
# 
# Path: pytube/monostate.py
# class Monostate:
#     def __init__(
#         self,
#         on_progress: Optional[Callable[[Any, bytes, int], None]],
#         on_complete: Optional[Callable[[Any, Optional[str]], None]],
#         title: Optional[str] = None,
#         duration: Optional[int] = None,
#     ):
#         self.on_progress = on_progress
#         self.on_complete = on_complete
#         self.title = title
#         self.duration = duration


import logging
import os
from datetime import datetime
from typing import BinaryIO, Dict, Optional, Tuple
from urllib.error import HTTPError
from urllib.parse import parse_qs
from pytube import extract, request
from pytube.helpers import safe_filename, target_directory
from pytube.itags import get_format_profile
from pytube.monostate import Monostate
"""
This module contains a container for stream manifest data.

A container object for the media stream (video only / audio only / video+audio
combined). This was referred to as ``Video`` in the legacy pytube version, but
has been renamed to accommodate DASH (which serves the audio and video
separately).
"""


logger = logging.getLogger(__name__)


class Stream:
    """Container for stream manifest data."""

    def __init__(

# Path: pytube/extract.py
# def publish_date(watch_html: str):
# def recording_available(watch_html):
# def is_private(watch_html):
# def is_age_restricted(watch_html: str) -> bool:
# def playability_status(watch_html: str) -> (str, str):
# def video_id(url: str) -> str:
# def playlist_id(url: str) -> str:
# def channel_name(url: str) -> str:
# def video_info_url(video_id: str, watch_url: str) -> str:
# def video_info_url_age_restricted(video_id: str, embed_html: str) -> str:
# def _video_info_url(params: OrderedDict) -> str:
# def js_url(html: str) -> str:
# def mime_type_codec(mime_type_codec: str) -> Tuple[str, List[str]]:
# def get_ytplayer_js(html: str) -> Any:
# def get_ytplayer_config(html: str) -> Any:
# def get_ytcfg(html: str) -> str:
# def apply_signature(stream_manifest: Dict, vid_info: Dict, js: str) -> None:
# def apply_descrambler(stream_data: Dict) -> None:
# def initial_data(watch_html: str) -> str:
# def initial_player_response(watch_html: str) -> str:
# def metadata(initial_data) -> Optional[YouTubeMetadata]:
# 
# Path: pytube/request.py
# def _execute_request(
#     url,
#     method=None,
#     headers=None,
#     data=None,
#     timeout=socket._GLOBAL_DEFAULT_TIMEOUT
# ):
# def get(url, extra_headers=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
# def post(url, extra_headers=None, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
# def seq_stream(
#     url,
#     timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
#     max_retries=0
# ):
# def stream(
#     url,
#     timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
#     max_retries=0
# ):
# def filesize(url):
# def seq_filesize(url):
# def head(url):
# 
# Path: pytube/helpers.py
# def safe_filename(s: str, max_length: int = 255) -> str:
#     """Sanitize a string making it safe to use as a filename.
# 
#     This function was based off the limitations outlined here:
#     https://en.wikipedia.org/wiki/Filename.
# 
#     :param str s:
#         A string to make safe for use as a file name.
#     :param int max_length:
#         The maximum filename character length.
#     :rtype: str
#     :returns:
#         A sanitized string.
#     """
#     # Characters in range 0-31 (0x00-0x1F) are not allowed in ntfs filenames.
#     ntfs_characters = [chr(i) for i in range(0, 31)]
#     characters = [
#         r'"',
#         r"\#",
#         r"\$",
#         r"\%",
#         r"'",
#         r"\*",
#         r"\,",
#         r"\.",
#         r"\/",
#         r"\:",
#         r'"',
#         r"\;",
#         r"\<",
#         r"\>",
#         r"\?",
#         r"\\",
#         r"\^",
#         r"\|",
#         r"\~",
#         r"\\\\",
#     ]
#     pattern = "|".join(ntfs_characters + characters)
#     regex = re.compile(pattern, re.UNICODE)
#     filename = regex.sub("", s)
#     return filename[:max_length].rsplit(" ", 0)[0]
# 
# def target_directory(output_path: Optional[str] = None) -> str:
#     """
#     Function for determining target directory of a download.
#     Returns an absolute path (if relative one given) or the current
#     path (if none given). Makes directory if it does not exist.
# 
#     :type output_path: str
#         :rtype: str
#     :returns:
#         An absolute directory path as a string.
#     """
#     if output_path:
#         if not os.path.isabs(output_path):
#             output_path = os.path.join(os.getcwd(), output_path)
#     else:
#         output_path = os.getcwd()
#     os.makedirs(output_path, exist_ok=True)
#     return output_path
# 
# Path: pytube/itags.py
# def get_format_profile(itag: int) -> Dict:
#     """Get additional format information for a given itag.
# 
#     :param str itag:
#         YouTube format identifier code.
#     """
#     itag = int(itag)
#     if itag in ITAGS:
#         res, bitrate = ITAGS[itag]
#     else:
#         res, bitrate = None, None
#     return {
#         "resolution": res,
#         "abr": bitrate,
#         "is_live": itag in LIVE,
#         "is_3d": itag in _3D,
#         "is_hdr": itag in HDR,
#         "is_dash": (
#             itag in DASH_AUDIO
#             or itag in DASH_VIDEO
#         ),
#     }
# 
# Path: pytube/monostate.py
# class Monostate:
#     def __init__(
#         self,
#         on_progress: Optional[Callable[[Any, bytes, int], None]],
#         on_complete: Optional[Callable[[Any, Optional[str]], None]],
#         title: Optional[str] = None,
#         duration: Optional[int] = None,
#     ):
#         self.on_progress = on_progress
#         self.on_complete = on_complete
#         self.title = title
#         self.duration = duration


# Path: pytube/streams.py
import logging
import os
from datetime import datetime
from typing import BinaryIO, Dict, Optional, Tuple
from urllib.error import HTTPError
from urllib.parse import parse_qs
from pytube import extract, request
from pytube.helpers import safe_filename, target_directory
from pytube.itags import get_format_profile
from pytube.monostate import Monostate

"""
This module contains a container for stream manifest data.

A container object for the media stream (video only / audio only / video+audio
combined). This was referred to as ``Video`` in the legacy pytube version, but
has been renamed to accommodate DASH (which serves the audio and video
separately).
"""


logger = logging.getLogger(__name__)


class Stream:
    """Container for stream manifest data."""

    def __init__(

        self, stream: Dict, monostate: Monostate