def get_youtube_handle(keys): """ Returns the YouTube Data API v3 handle. Parameter --------- api_key: (str, list(str)) An index and a list of authorized API keys. Returns ------- An object for interacting with the YouTube API v3 service. """ try: index = keys[0] key = keys[1][index] keys[0] += 1 except IndexError: echow("No API Keys left!") if click.confirm("Do you want to enter another API key?"): key = click.prompt("Enter your API key", type=str) else: echoe("No API key provided. Exiting.") try: return build("youtube", "v3", developerKey=key) except HttpError as e: echoe(f""" There was an error while connecting to the YouTube API. Please check your API key: {e}""")
def get(context, option): """ Shows a default option.""" config = context.obj["config"] update_config(config) if option in config: echov(f"The value of '{option}' is set to '{config[option]}'.") else: echow(f"The value of '{option}' is not set!")
def _search(handle, number, **api_options): api_options["type"] = "video" api_options["part"] = "id,snippet" response = _get_response(handle.search(), **api_options) video_data = [_extract_data(video) for video in response["items"]] if len(video_data) < number: echow( f"API gave less videos than requested: {len(video_data)} instead of {number}!" ) return video_data[:number]
def update_config(config, options={}): """ Updates a configuration dictionary and inserts default values. Parameters ---------- config: dict A dictionary that should be updated. options: dict Values that should be put into `config`. """ for key, value in options.items(): if value or value == 0: if key in DEFAULT_OPTIONS: config[key] = value else: echow(f"Invalid option given: {key}") for key in DEFAULT_OPTIONS: if key not in config: config[key] = DEFAULT_OPTIONS[key]
def load_config(config_path=None): r"""Processes and returns the user configuration. This function reads a TOML configuration file from either a provided path or the standard configuration directory and returns it. Parameters ---------- config_path: str, optional The file path to the configuration file. If not specified, the method tries to read the default system-specific configuration directory. Returns ------- dict Configuration dictionary. Notes ----- The standard configuration directory is system specific: - Mac OS X: "~/Library/Application Support/YouTube Scraper" - Unix: "~/.config/youtube-scraper" - Windows: C:\Users\<user>\AppData\Roaming\YouTube Scraper """ if config_path: config = toml.load(config_path) else: try: config_path = _get_default_config_path() config = toml.load(config_path) except FileNotFoundError: echow("Configuration file not found:") echow(str(config_path)) if click.confirm("Do you want to create a default configuration?"): config_path = _get_default_config_path(create=True) config = toml.load(config_path) else: config = {} config["config_path"] = config_path return config
def build_nodes(config, handle, api_options, starter_videos): for rank, video in enumerate(starter_videos): video.update({"rank": rank, "depth": 0}) queue = deque(starter_videos) processed = [] processed_ids = set() while len(queue) > 0: video = queue.popleft() echov( f"Processing video {video['videoId']} (Depth: {video['depth']}).", verbose, ) processed.append(video) processed_ids.add(video["videoId"]) if video["depth"] >= config["max_depth"]: video["relatedVideos"] = list() continue # Add children num_children = _get_branching(config["number"], video["depth"]) while True: try: children = related_search(handle, num_children, video["videoId"], **api_options) video["relatedVideos"] = list( map(lambda c: c["videoId"], children)) for rank, child in enumerate(children): child.update({"rank": rank, "depth": video["depth"] + 1}) if config["unique"]: queue.extend(child for child in children if child["videoId"] not in processed_ids) else: queue.extend(children) break except HttpError as e: sys.tracebacklimit = 0 echow("Http error received:") echow(e) handle = get_youtube_handle(api_options["keys"]) return processed