コード例 #1
0
async def holo_heartbeat(DatabaseConn: VTBiliDatabase, JetriConn: Jetri,
                         room_dataset: dict):
    session = aiohttp.ClientSession(headers={"User-Agent": CHROME_UA})

    vtlog.info("Fetching local youtube data...")
    holo_lives, holo_upcome = await JetriConn.fetch_lives()

    vtlog.info("Collecting live channels on youtube...")
    collect_live_channels = []
    for live_data in holo_lives:
        ch_id = live_data["channel"]
        if ch_id not in collect_live_channels:
            vtlog.debug(f"|--> Adding: {ch_id}")
            collect_live_channels.append(ch_id)
    for up_data in holo_upcome:
        ch_id = up_data["channel"]
        current_time = datetime.now(tz=timezone.utc).timestamp() - 300
        if current_time >= up_data["startTime"]:
            if ch_id not in collect_live_channels:
                vtlog.debug(f"|--> Adding: {ch_id}")
                collect_live_channels.append(ch_id)

    holo_data: dict = room_dataset["holo"]
    vtlog.info("Fetching ignored room data from database...")
    # await DatabaseConn.fetch_data("hololive_ignored")
    is_db_fetched = False
    try:
        db_holo_ignored: dict = await asyncio.wait_for(
            DatabaseConn.fetch_data("hololive_ignored"), 15.0)
        is_db_fetched = True
    except asyncio.TimeoutError:
        await DatabaseConn.release()
        DatabaseConn.raise_error()
        vtlog.error(
            "Failed to fetch Hololive Ignored database, using blank data.")
        db_holo_ignored = {"data": []}
    holo_ignored: list = db_holo_ignored["data"]

    vtlog.info("Creating tasks to check room status...")
    room_to_fetch = [fetch_room(session, room) for room in holo_data.keys()]
    vtlog.info("Firing API requests!")
    final_results = []
    for froom in asyncio.as_completed(room_to_fetch):
        room_data, room_id = await froom
        vtlog.debug(f"|-- Checking heartbeat for: {room_id}")
        if not room_data:
            vtlog.warn(f"|--! Failed fetching Room ID: {room_id} skipping")
            continue
        if room_data["live_status"] != 1:
            continue
        thumbnail = room_data["user_cover"]
        viewers = room_data["online"]
        start_time = int(
            round(
                datetime.strptime(room_data["live_time"] + " +0800",
                                  "%Y-%m-%d %H:%M:%S %z").timestamp())) - (
                                      8 * 60 * 60)  # Set to UTC
        gen_id = f"bili{room_id}_{start_time}"
        if gen_id in holo_ignored:
            vtlog.warn(f"Ignoring {room_id} since it's an Ignored restream...")
            continue
        if str(room_id) in holo_data:
            holo_map = holo_data[str(room_id)]
            if "id" in holo_map and holo_map["id"] in collect_live_channels:
                vtlog.warn(
                    f"Ignoring {room_id} since it's a YouTube restream...")
                if gen_id not in holo_ignored:
                    holo_ignored.append(gen_id)
                continue
        vtlog.info(f"Adding room_id: {room_id}")
        dd = {
            "id": gen_id,
            "room_id": int(room_id),
            "title": room_data["title"],
            "startTime": start_time,
            "channel": str(room_data["uid"]),
            "channel_name": holo_data[str(room_id)]["name"],
            "thumbnail": thumbnail,
            "viewers": viewers,
            "platform": "bilibili",
        }
        final_results.append(dd)

    if final_results:
        final_results.sort(key=lambda x: x["startTime"])

    vtlog.info("Updating database...")
    upd_data = {"live": final_results}
    upd_data2 = {"data": holo_ignored}
    try:
        await asyncio.wait_for(
            DatabaseConn.update_data("hololive_data", upd_data), 15.0)
    except asyncio.TimeoutError:
        await DatabaseConn.release()
        DatabaseConn.raise_error()
        vtlog.error(
            "Failed to update Hololive Heartbeat data, timeout by 15s...")
    if is_db_fetched:
        try:
            await asyncio.wait_for(
                DatabaseConn.update_data("hololive_ignored", upd_data2), 15.0)
        except asyncio.TimeoutError:
            await DatabaseConn.release()
            DatabaseConn.raise_error()
            vtlog.error(
                "Failed to update Hololive ignored database, timeout by 15s..."
            )
    await session.close()
コード例 #2
0
async def youtube_video_feeds(DatabaseConn: VTBiliDatabase, dataset: dict,
                              yt_api_key: RotatingAPIKey):
    sessions = aiohttp.ClientSession(
        headers={"User-Agent": "VTBSchedule/0.9.0"})

    vtlog.info("Fetching saved live data...")
    try:
        youtube_lives_data: dict = await asyncio.wait_for(
            DatabaseConn.fetch_data("yt_other_livedata"), 15.0)
    except asyncio.TimeoutError:
        await DatabaseConn.release()
        DatabaseConn.raise_error()
        vtlog.error("Failed to fetch youtube live database, skipping run.")
        await sessions.close()
        return
    del youtube_lives_data["_id"]

    vtlog.info("Fetching all fetched video IDs...")
    fetched_video_ids: dict = {}
    for channel, channel_data in youtube_lives_data.items():
        if channel not in fetched_video_ids:
            fetched_video_ids[channel] = []
        for video in channel_data:
            if video["id"] not in fetched_video_ids[channel]:
                fetched_video_ids[channel].append(video["id"])

    try:
        ended_video_ids = await asyncio.wait_for(
            DatabaseConn.fetch_data("yt_other_ended_ids"), 15.0)
        del ended_video_ids["_id"]
        for channel, channel_data in ended_video_ids.items():
            if channel not in fetched_video_ids:
                fetched_video_ids[channel] = []
            for video in channel_data:
                if video not in fetched_video_ids[channel]:
                    fetched_video_ids[channel].append(video)
    except asyncio.TimeoutError:
        await DatabaseConn.release()
        DatabaseConn.raise_error()
        vtlog.warning(
            "Failed to fetch youtube ended id database, skipping run.")
        await sessions.close()
        return

    vtlog.info("Creating job task for xml files.")
    xmls_to_fetch = [
        fetch_xmls(sessions, chan["id"], chan["affiliates"], nn)
        for nn, chan in enumerate(dataset)
    ]
    collected_videos_ids = {}
    vtlog.info("Firing xml fetching!")
    for xmls in asyncio.as_completed(xmls_to_fetch):
        feed_results, channel, affliate, nn = await xmls

        fetched_videos = []
        if channel in fetched_video_ids:
            fetched_videos = fetched_video_ids[channel]

        vtlog.info(f"|=> Processing XMLs: {dataset[nn]['name']}")
        video_ids = []
        for entry in feed_results.entries:
            ids_ = entry["yt_videoid"]
            if ids_ not in fetched_videos:
                video_ids.append(ids_)

        collected_videos_ids[channel + "//" + affliate] = video_ids

    vtlog.info("Collected!")
    vtlog.info("Now creating tasks for a non-fetched Video IDs to the API.")

    video_to_fetch = []
    for chan_aff, videos in collected_videos_ids.items():
        chan, aff = chan_aff.split("//")
        if not videos:
            vtlog.warn(f"Skipping: {chan} since there's no video to fetch.")
            continue
        param = {
            "part": "snippet,liveStreamingDetails",
            "id": ",".join(videos),
            "key": yt_api_key.get(),
        }
        vtlog.info(f"|-- Processing: {chan}")
        video_to_fetch.append(fetch_apis(sessions, "videos", param, chan, aff))

    if not video_to_fetch:
        vtlog.warn("|== No video to fetch, bailing!")
        await sessions.close()
        return 0

    vtlog.info("Firing API fetching!")
    time_past_limit = current_time() - (6 * 60 * 60)
    for task in asyncio.as_completed(video_to_fetch):
        video_results, ch_id, affliate = await task
        if ch_id not in youtube_lives_data:
            youtube_lives_data[ch_id] = []
        if ch_id not in ended_video_ids:
            ended_video_ids[ch_id] = []
        vtlog.info(f"|== Parsing videos data for: {ch_id}")
        youtube_videos_data = youtube_lives_data[ch_id]
        for res_item in video_results["items"]:
            video_id = res_item["id"]
            if "liveStreamingDetails" not in res_item:
                # Assume normal video
                ended_video_ids[ch_id].append(video_id)
                continue
            snippets = res_item["snippet"]
            livedetails = res_item["liveStreamingDetails"]
            if not livedetails:
                # Assume normal video
                ended_video_ids[ch_id].append(video_id)
                continue
            broadcast_cnt = snippets["liveBroadcastContent"]
            if not broadcast_cnt:
                broadcast_cnt = "unknown"
            if broadcast_cnt not in ("live", "upcoming"):
                broadcast_cnt = "unknown"

            title = snippets["title"]
            channel = snippets["channelId"]
            start_time = 0
            if "scheduledStartTime" in livedetails:
                start_time = datetime_yt_parse(
                    livedetails["scheduledStartTime"])
            if "actualStartTime" in livedetails:
                start_time = datetime_yt_parse(livedetails["actualStartTime"])
            thumbs = f"https://i.ytimg.com/vi/{video_id}/maxresdefault.jpg"

            dd_hell = {
                "id": video_id,
                "title": title,
                "status": broadcast_cnt,
                "startTime": start_time,
                "endTime": None,
                "thumbnail": thumbs,
                "group": affliate,
                "platform": "youtube",
            }
            if "actualEndTime" in livedetails:
                dd_hell["endTime"] = datetime_yt_parse(
                    livedetails["actualEndTime"])
                dd_hell["status"] = "past"

            if dd_hell["status"] == "past" and time_past_limit >= dd_hell[
                    "endTime"]:
                vtlog.warning(
                    f"Removing: {video_id} since it's way past the time limit."
                )
                ended_video_ids[ch_id].append(video_id)
                continue

            vtlog.info("Adding: {}".format(video_id))
            youtube_videos_data.append(dd_hell)

        youtube_lives_data[ch_id] = youtube_videos_data
        vtlog.info(f"|== Updating database ({ch_id})...")
        upd_data = {ch_id: youtube_videos_data}
        try:
            await asyncio.wait_for(
                DatabaseConn.update_data("yt_other_livedata", upd_data), 15.0)
        except asyncio.TimeoutError:
            await DatabaseConn.release()
            DatabaseConn.raise_error()
            vtlog.error(
                f"Failed to fetch update live data for {ch_id}, timeout by 15s..."
            )

    try:
        await asyncio.wait_for(
            DatabaseConn.update_data("yt_other_ended_ids", ended_video_ids),
            15.0)
    except asyncio.TimeoutError:
        await DatabaseConn.release()
        DatabaseConn.raise_error()
        vtlog.error("Failed to update ended video ids, timeout by 15s...")

    vtlog.info("Closing sessions...")
    await sessions.close()
コード例 #3
0
async def niji_heartbeat(DatabaseConn: VTBiliDatabase,
                         VTNijiConn: VTBiliDatabase, room_dataset: dict):
    session = aiohttp.ClientSession(headers={"User-Agent": CHROME_UA})

    vtlog.info("Fetching currently live/upcoming data from VTNiji Database...")
    collect_live_channels: list = []
    try:
        niji_yt_puredata = await asyncio.wait_for(
            VTNijiConn.fetch_data("nijitube_live"), 15.0)
        del niji_yt_puredata["_id"]
        for channel_id, channel_data in niji_yt_puredata.items():
            for vtu in channel_data:
                current_time = datetime.now(tz=timezone.utc).timestamp() - 300
                if vtu["status"] == "live":
                    if channel_id not in collect_live_channels:
                        collect_live_channels.append(channel_id)
                elif vtu["status"] == "upcoming":
                    if current_time >= vtu["startTime"]:
                        if channel_id not in collect_live_channels:
                            collect_live_channels.append(channel_id)
    except asyncio.TimeoutError:
        await DatabaseConn.release()
        DatabaseConn.raise_error()
        vtlog.error(
            "Failed to fetch live/upcoming data from VTNiji Database, timeout by 15s..."
        )
    niji_data: dict = room_dataset["niji"]
    vtlog.info("Fetching ignored room data from database...")
    is_db_fetched = False
    try:
        db_niji_ignored: dict = await asyncio.wait_for(
            DatabaseConn.fetch_data("nijisanji_ignored"), 15.0)
        is_db_fetched = True
    except asyncio.TimeoutError:
        await DatabaseConn.release()
        DatabaseConn.raise_error()
        vtlog.error(
            "Failed to fetch Nijisanji Ignored database, using blank data.")
        db_niji_ignored = {"data": []}
    niji_ignored: list = db_niji_ignored["data"]

    vtlog.info("Creating tasks to check room status...")
    room_to_fetch = [fetch_room(session, room) for room in niji_data.keys()]
    vtlog.info("Firing API requests!")
    final_results = []
    for froom in asyncio.as_completed(room_to_fetch):
        room_data, room_id = await froom
        vtlog.debug(f"|-- Checking heartbeat for: {room_id}")
        if not room_data:
            vtlog.warn(f"|--! Failed fetching Room ID: {room_id} skipping")
            continue
        if room_data["live_status"] != 1:
            continue
        thumbnail = room_data["user_cover"]
        viewers = room_data["online"]
        start_time = int(
            round(
                datetime.strptime(room_data["live_time"] + " +0800",
                                  "%Y-%m-%d %H:%M:%S %z").timestamp())) - (
                                      8 * 60 * 60)  # Set to UTC.
        gen_id = f"bili{room_id}_{start_time}"
        if gen_id in niji_ignored:
            vtlog.warn(f"Ignoring {room_id} since it's an Ignored restream...")
            continue
        if str(room_id) in niji_data:
            niji_map = niji_data[str(room_id)]
            if "id" in niji_map and niji_map["id"] in collect_live_channels:
                vtlog.warn(
                    f"Ignoring {room_id} since it's a YouTube restream...")
                if gen_id not in niji_ignored:
                    niji_ignored.append(gen_id)
                continue
        # hls_list, _ = await fetch_room_hls(session, str(room_id))
        dd = {
            "id": gen_id,
            "room_id": int(room_id),
            "title": room_data["title"],
            "startTime": start_time,
            "channel": str(room_data["uid"]),
            "channel_name": niji_data[str(room_id)]["name"],
            "thumbnail": thumbnail,
            "viewers": viewers,
            "platform": "bilibili",
        }
        final_results.append(dd)

    if final_results:
        final_results.sort(key=lambda x: x["startTime"])

    vtlog.info("Updating database...")
    upd_data = {"live": final_results}
    upd_data2 = {"data": niji_ignored}
    try:
        await asyncio.wait_for(
            DatabaseConn.update_data("nijisanji_data", upd_data), 15.0)
    except asyncio.TimeoutError:
        await DatabaseConn.release()
        DatabaseConn.raise_error()
        vtlog.error(
            "Failed to update Nijisanji Heartbeat data, timeout by 15s...")
    if is_db_fetched:
        try:
            await asyncio.wait_for(
                DatabaseConn.update_data("nijisanji_ignored", upd_data2), 15.0)
        except asyncio.TimeoutError:
            await DatabaseConn.release()
            DatabaseConn.raise_error()
            vtlog.error(
                "Failed to update Nijisanji ignored database, timeout by 15s..."
            )
    await session.close()
コード例 #4
0
async def youtube_live_heartbeat(DatabaseConn: VTBiliDatabase,
                                 affliates_dataset: dict,
                                 yt_api_key: RotatingAPIKey):
    session = aiohttp.ClientSession(
        headers={"User-Agent": "VTBSchedule/0.9.0"})

    vtlog.info("Fetching live data...")

    try:
        youtube_lives_data = await asyncio.wait_for(
            DatabaseConn.fetch_data("yt_other_livedata"), 15.0)
        ended_video_ids = await asyncio.wait_for(
            DatabaseConn.fetch_data("yt_other_ended_ids"), 15.0)
    except asyncio.TimeoutError:
        await DatabaseConn.release()
        DatabaseConn.raise_error()
        vtlog.error("Failed to fetch youtube live database, skipping run.")
        await session.close()
        return
    del youtube_lives_data["_id"]

    videos_list = []
    videos_set = {}
    for cid, data in youtube_lives_data.items():
        for vd in data:
            if vd["status"] in ("unknown"):
                continue
            videos_list.append(vd["id"])
            videos_set[vd["id"]] = cid

    if not videos_list:
        vtlog.warn("No live/upcoming videos, bailing!")
        await session.close()
        return 0

    chunked_videos_list = [
        videos_list[i:i + 40] for i in range(0, len(videos_list), 40)
    ]
    items_data_data = []
    for chunk_n, chunk_list in enumerate(chunked_videos_list, 1):
        vtlog.info(
            f"Checking heartbeat for chunk {chunk_n} out of {len(chunked_videos_list)} chunks"
        )
        param = {
            "part": "snippet,liveStreamingDetails",
            "id": ",".join(chunk_list),
            "key": yt_api_key.get(),
        }
        items_data, _, _ = await fetch_apis(session, "videos", param,
                                            "nullify", "nullify")
        items_data_data.extend(items_data["items"])
    await session.close()

    parsed_ids = {}
    vtlog.info("Parsing results...")
    time_past_limit = current_time() - (6 * 60 * 60)
    for res_item in items_data_data:
        video_id = res_item["id"]
        vtlog.info(f"|-- Checking {video_id} heartbeat...")
        snippets = res_item["snippet"]
        channel_id = snippets["channelId"]
        if channel_id not in ended_video_ids:
            ended_video_ids[channel_id] = []
        if "liveStreamingDetails" not in res_item:
            continue
        livedetails = res_item["liveStreamingDetails"]
        status_live = "upcoming"
        start_time = 0
        end_time = 0
        if "scheduledStartTime" in livedetails:
            start_time = datetime_yt_parse(livedetails["scheduledStartTime"])
        if "actualStartTime" in livedetails:
            status_live = "live"
            start_time = datetime_yt_parse(livedetails["actualStartTime"])
        if "actualEndTime" in livedetails:
            status_live = "past"
            end_time = datetime_yt_parse(livedetails["actualEndTime"])
        view_count = None
        if "concurrentViewers" in livedetails:
            view_count = livedetails["concurrentViewers"]
            try:
                view_count = int(view_count)
            except ValueError:
                pass
        thumbs = f"https://i.ytimg.com/vi/{video_id}/maxresdefault.jpg"
        vtlog.info(f"|--> Update status for {video_id}: {status_live}")
        new_streams_data = []
        for data_streams in youtube_lives_data[channel_id]:
            if "group" not in data_streams:
                data_streams["group"] = affliates_dataset[channel_id]
            if data_streams["id"] == video_id:
                append_data = {
                    "id": data_streams["id"],
                    "title": snippets["title"],
                    "status": status_live,
                    "startTime": start_time,
                    "endTime": None,
                    "group": data_streams["group"],
                    "thumbnail": thumbs,
                    "platform": "youtube",
                }
                if view_count is not None:
                    append_data["viewers"] = view_count
                if status_live == "past":
                    append_data["endTime"] = end_time
                if status_live == "past" and time_past_limit >= end_time:
                    vtlog.warning(
                        f"Removing: {video_id} since it's way past the time limit."
                    )
                    ended_video_ids[channel_id].append(video_id)
                    continue
                new_streams_data.append(append_data)
            else:
                if data_streams["status"] == "past":
                    if time_past_limit >= data_streams["endTime"]:
                        vtlog.warning(
                            f"Removing: {video_id} since it's way past the time limit."
                        )
                        ended_video_ids[channel_id].append(video_id)
                        continue
                new_streams_data.append(data_streams)
        new_streams_data = await check_for_doubles(new_streams_data)
        youtube_lives_data[channel_id] = new_streams_data
        vtlog.info(f"|-- Updating heartbeat for channel {channel_id}...")
        try:
            await asyncio.wait_for(
                DatabaseConn.update_data("yt_other_livedata",
                                         {channel_id: new_streams_data}), 15.0)
        except asyncio.TimeoutError:
            await DatabaseConn.release()
            DatabaseConn.raise_error()
            vtlog.error(
                f"|--! Failed to update heartbeat for channel {channel_id}, timeout by 15s..."
            )
        parsed_ids[video_id] = channel_id

    # Filter this if the video is privated.
    parsed_ids_keys = list(parsed_ids.keys())
    for video in videos_list:
        if video not in parsed_ids_keys:
            chan_id = videos_set[video]
            channel_data = youtube_lives_data[chan_id]
            new_channel_data = []
            for ch_vid in channel_data:
                if ch_vid["id"] != video:
                    new_channel_data.append(ch_vid)
                else:
                    if chan_id not in ended_video_ids:
                        ended_video_ids[chan_id] = []
                    ended_video_ids[chan_id].append(ch_vid["id"])
            vtlog.info(
                f"|-- Updating heartbeat filter for channel {chan_id}...")
            try:
                await asyncio.wait_for(
                    DatabaseConn.update_data("yt_other_livedata",
                                             {chan_id: new_channel_data}),
                    15.0)
            except asyncio.TimeoutError:
                await DatabaseConn.release()
                DatabaseConn.raise_error()
                vtlog.error(
                    f"|--! Failed to update heartbeat for channel {chan_id}, timeout by 15s..."
                )

    try:
        await asyncio.wait_for(
            DatabaseConn.update_data("yt_other_ended_ids", ended_video_ids),
            15.0)
    except asyncio.TimeoutError:
        await DatabaseConn.release()
        DatabaseConn.raise_error()
        vtlog.error("Failed to update ended video ids, timeout by 15s...")