def parse_json(json_string, transform_source=None) -> dict or None:
    """
    Taken and have been edited from:
        https://github.com/ytdl-org/youtube-dl/blob/master/youtube_dl/extractor/common.py#L895
    """
    if json_string:
        if transform_source:
            json_string = transform_source(json_string)
        try:
            return json.loads(json_string)
        except ValueError:
            warning("Failed to parse JSON.")
            error_warning(traceback.format_exc())
            return None
    return None
def loadServer(process_handler,
               cached_data_handler,
               port,
               youtube_api_handler,
               cert=None,
               key=None):
    try:
        from gevent.pywsgi import WSGIServer as web_server
    except ImportError:
        error_warning("Get gevent package! Unable to run.")
        web_server = None
    if web_server:
        ssl_args = dict()
        if cert:
            ssl_args['certfile'] = cert
        if key:
            ssl_args['keyfile'] = key
        app = Server(__name__, process_handler, cached_data_handler,
                     youtube_api_handler)

        @app.before_request
        def before_request():
            user_agent = request.headers.get('User-Agent')
            client_header = request.headers.get('Client')
            if not (user_agent
                    and 'WEB-CLIENT' in user_agent) and not client_header:
                rule = request.url_rule
                if rule is not None:
                    url = rule.rule
                    if not ('login' in url and request.args.get('unlockCode')
                            is not None) and 'callback' not in url:
                        return '', 403

        http_server = web_server(('0.0.0.0', port), app, **ssl_args)

        info("Starting server. Hosted on port: {0}!".format(port))
        http_server.serve_forever()
    def uploadYouTube():
        try:
            # youtube_api_handler.initialize_upload()
            def get_upload_settings():
                upload_settings = youtube_api_handler.getCacheDataHandler(
                ).getValue('UploadSettings')
                channel_name = video_data.get('channel_name')
                if channel_name:
                    if channel_name in upload_settings:
                        return upload_settings[channel_name]
                return upload_settings[None]

            def _replace_variables(text):
                class DataDict(dict):
                    """
                        Taken from and
                        have been edited: https://stackoverflow.com/a/11023271
                    """
                    def __missing__(self, key):
                        return ''

                now = video_data.get('start_date')  # type: datetime
                timezone = getTimeZone()
                if text is not None:
                    text = text.format(**DataDict(
                        VIDEO_ID=video_id,
                        FILENAME=file_locations[0],
                        CHANNEL_ID=video_data.get('channel_id'),
                        CHANNEL_NAME=video_data.get('channel_name'),
                        START_DATE_MONTH=str(now.month),
                        START_DATE_DAY=str(now.day),
                        START_DATE_YEAR=str(now.year),
                        START_DATE="{0}/{1}/{2}".format(
                            now.month, now.day, now.year),
                        START_TIME=str(now.strftime("%I:%M %p")),
                        TIMEZONE=timezone if timezone is not None else '',
                        TITLE=str(video_data.get('title')),
                        DESCRIPTION=str(video_data.get('description'))))
                    if text is None:
                        return "[FAILED TO REPLACE VARIABLES]"
                return text

            settings = get_upload_settings()

            if youtube_api_handler.getCacheDataHandler().getValue(
                    'UploadLiveStreams') is True:
                upload_video_id = youtube_api_handler.initialize_upload(
                    file_locations[0], _replace_variables(settings['title']),
                    _replace_variables('\n'.join(settings['description'])),
                    _replace_variables(settings['tags']),
                    settings['CategoryID'], settings['privacyStatus'])
                sleep(3)
                if youtube_api_handler.getCacheDataHandler().getValue(
                        'UploadThumbnail') is True:
                    info("Uploading Thumbnail for {0}".format(
                        video_data.get('channel_name')))
                    youtube_api_handler.upload_thumbnail(upload_video_id, None)
                    # TODO Get Auto Uploading thumbnails! working again!
                    info("Thumbnail Done Uploading!")
                return [True, None]
        except:
            traceback_ = traceback.format_exc()
            if 'quota' in traceback_ and 'usage' in traceback_:
                return [
                    False,
                    "YouTube API Quota has been exceeded. Waiting until YouTube API Quota resets."
                ]
            error_warning(traceback_)
            warning("Unable to upload stream to Youtube.")
            return [False, traceback]
def is_live(channel_Class, CookieDict=None, globalVariables=None, json=None):
    """

    Checks if channel is live using the normal Youtube heartbeat.
    Also sets heartbeat related variables.

    :type CookieDict: dict
    :type channel_Class: TemplateChannel
    :type globalVariables: GlobalVariables

    """

    if globalVariables is None:
        globalVariables = TempGlobalVariables()

    try:
        if json is None:
            referer_url = 'https://www.youtube.com/channel/{0}/live'.format(
                channel_Class.channel_id)
            headers = {
                'Accept': "*/*",
                'Accept-Language': 'en-US,en;q=0.9',
                'dnt': '1',
                'referer': referer_url,
                'x-youtube-client-name': '1'
            }
            url_arguments = {
                'video_id':
                channel_Class.video_id,
                'heartbeat_token':
                '',
                'c':
                (globalVariables.get("client_name")
                 if globalVariables.get("client_name") is not None else 'WEB'),
                'sequence_number':
                str(channel_Class.sequence_number)
            }
            if globalVariables.get("account_playback_token") is not None:
                headers.update({
                    'x-youtube-identity-token':
                    globalVariables.get("account_playback_token")
                })
            if globalVariables.get("page_build_label") is not None:
                headers.update({
                    'x-youtube-page-label':
                    globalVariables.get("page_build_label")
                })
            if globalVariables.get("page_cl") is not None:
                headers.update(
                    {'x-youtube-page-cl': globalVariables.get("page_cl")})
            if globalVariables.get("variants_checksum") is not None:
                headers.update({
                    'x-youtube-variants-checksum':
                    globalVariables.get("variants_checksum")
                })
            if globalVariables.get("utf_offset") is not None:
                headers.update({
                    'x-youtube-utc-offset':
                    str(globalVariables.get("utf_offset"))
                })
                url_arguments.update({
                    'utc_offset_minutes':
                    str(globalVariables.get("utf_offset"))
                })
            if globalVariables.get("client_version") is not None:
                headers.update({
                    'x-youtube-client-version':
                    globalVariables.get("client_version")
                })
                url_arguments.update(
                    {'cver': str(globalVariables.get("client_version"))})
            if globalVariables.get("timezone") is not None:
                url_arguments.update(
                    {'time_zone': str(globalVariables.get("timezone"))})
            if channel_Class.cpn is not None:
                url_arguments.update({'cpn': channel_Class.cpn})

            websiteClass = download_website(
                'https://www.youtube.com/heartbeat?{0}'.format(
                    urlencode(url_arguments)),
                headers=headers,
                CookieDict=CookieDict)
            CookieDict.update(websiteClass.cookies)
            if websiteClass.text is None:
                return None
            json = websiteClass.parse_json()

        channel_Class.sequence_number += 1
        reply('FROM YOUTUBE -> {0}'.format(json))

        # SETTING VARIABLES
        liveStreamAbilityRenderer = try_get(
            json,
            lambda x: x['liveStreamability']['liveStreamabilityRenderer'],
            dict)
        if liveStreamAbilityRenderer:
            thumbnail = get_thumbnail(liveStreamAbilityRenderer)
            if thumbnail:
                channel_Class.thumbnail_url = thumbnail
            channel_Class.pollDelayMs = get_poll_delay_ms(
                liveStreamAbilityRenderer, channel_Class)
            channel_Class.live_scheduled = get_unix_schedule_time(
                liveStreamAbilityRenderer) is not None
            channel_Class.broadcast_id = get_broadcast_id(
                liveStreamAbilityRenderer)
            video_id = get_video_id(liveStreamAbilityRenderer)
            if video_id:
                if video_id != channel_Class.video_id:
                    channel_Class.add_youtube_queue(
                    )  # just in case something happens.
                    channel_Class.video_id = video_id

        if channel_Class.live_scheduled is True:
            channel_Class.live_scheduled_timeString = get_schedule_time(
                liveStreamAbilityRenderer)
            unix_time = get_unix_schedule_time(liveStreamAbilityRenderer)
            if unix_time:
                channel_Class.live_scheduled_time = datetime.fromtimestamp(
                    unix_time)

        if 'stop_heartbeat' in json:
            channel_Class.add_youtube_queue()
            channel_Class.loadVideoData()
            return False

        if try_get(liveStreamAbilityRenderer, lambda x: x['displayEndscreen'],
                   bool):
            last_video_id = channel_Class.video_id
            channel_Class.loadVideoData()
            # CHECK IF A VIDEO ID CHANGE BEFORE ADDING.
            if last_video_id != channel_Class.video_id:
                channel_Class.add_youtube_queue()
            return False

        status = try_get(json, lambda x: x['status'], str)  # type: str
        if status:  # Sometimes status is removed and causes an error.
            if "OK" in status.upper():
                return True
            if "STOP" in status.upper():
                channel_Class.add_youtube_queue()
                channel_Class.loadVideoData()
                return False
            if "ERROR" in status.upper():
                warning("Getting the Live Data, failed on Youtube's Side. "
                        "Youtube Replied with: {0}.".format(json['reason']))
                return False
            if "LIVE_STREAM_OFFLINE" in status.upper():
                return False
            warning(
                "The Program couldn't find any value that matches the normal heartbeat. Returning False."
            )
        return False
    except KeyboardInterrupt:
        pass
    except BrokenPipeError:
        exit()
    except Exception:
        warning("Error occurred when doing Heartbeat.")
        error_warning(traceback.format_exc())
        return -1
    def remove_channel(self):
        def searchChannel():
            channel_dict_ = self.process_Handler.channels_dict.get(
                channel_identifier)
            if channel_dict_ is None:
                channel_array = [
                    channel_ for channel_ in self.process_Handler.channels_dict
                    if channel_identifier.casefold() ==
                    self.process_Handler.channels_dict.get(
                        channel_)['class'].get('channel_name').casefold()
                ]
                if channel_array is None or len(channel_array) == 0:
                    return [channel_identifier, None]
                return channel_array[
                    0], self.process_Handler.channels_dict.get(
                        channel_array[0])
            return channel_identifier, channel_dict_

        args = request.args  # type: ImmutableMultiDict
        channel_identifier = args.get("channel_id")
        if channel_identifier is None:
            channel_identifier = args.get("channel_identifier")
        if channel_identifier == '':
            return Response(
                'You need to specify a valid {0}.'.format(channel_identifier),
                status='client-error',
                status_code=400)
        if channel_identifier is None:
            return Response(
                "You need {0} in args.".format("channel_identifier"),
                status="client-error",
                status_code=400)

        channel_identifier, channel_dict = searchChannel()
        if channel_dict is None:
            return Response(
                "{0} hasn't been added to the channel list, so it can't be removed."
                .format(channel_identifier),
                status="server-error",
                status_code=500)
        if 'error' not in channel_dict:
            channel_dict['class'].close()
            thread_class = channel_dict['thread_class']
            try:
                thread_class.terminate()
                sleep(1)
                if thread_class.is_alive():
                    return Response("Unable to Terminate.",
                                    status="server-error",
                                    status_code=500)
            except Exception as e:
                error_warning(traceback.format_exc())
                return Response("Unable to remove channel. {0}".format(str(e)),
                                status="server-error",
                                status_code=500)
        platform_name = channel_dict['class'].get('platform_name')
        channels = self.cached_data_handler.getValue("channels")
        if channels is None:
            channels = {}
        if platform_name.upper() not in channels:
            channels.update({platform_name.upper(): []})
        list_ = channels.get(platform_name.upper())  # type: list
        list_.remove(channel_identifier)
        channels.update({platform_name.upper(): list_})
        self.cached_data_handler.setValue("channels", channels)
        del self.process_Handler.channels_dict[channel_identifier]
        sleep(.01)
        info("{0} has been removed.".format(channel_identifier))
        return Response(None)