diff --git a/example-config.yaml b/example-config.yaml index aeaa176..851ae78 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -12,3 +12,12 @@ config: channels: - UCL_qhgtOy0dy1Agp8vkySQg - UCHsx4Hqa-1ORjQTh9TYDhww + # If the invidious_instances key doesn't exist or all the instances fail, the youtube rss feed will be used as a fallback + invidious_instances: + - https://tube.connect.cafe + - https://invidious.zapashcanon.fr + - https://invidious.site + - https://invidious.048596.xyz + - https://vid.puffyan.us + - https://invidious.himiko.cloud + - https://invidious.silkky.cloud diff --git a/ytnotifier.py b/ytnotifier.py index c34d9eb..f3c6754 100644 --- a/ytnotifier.py +++ b/ytnotifier.py @@ -29,6 +29,7 @@ if config.get('storage'): notify_chat = config['config']['notify_chat'] wait_seconds = config['config']['wait_seconds'] channels = config['config']['channels'] +invidious_instances = config['config'].get('invidious_instances', []) live_regex = re.compile(r'error: (?:ytnotifier:([0-9]+) )?(this live event will begin|premieres) in (.+)', re.I) strip_date = re.compile(r' \d{4}-\d{2}-\d{2} \d{2}:\d{2}$') @@ -123,6 +124,20 @@ async def handle_video(video_id, video_title): finally: tmp_handled_videos.discard(video_id) +async def get_video_list(session, channel_id): + for i in invidious_instances: + try: + async with session.get(f'{i}/api/v1/channels/{channel_id}/latest?fields=title,videoId&a={time.time()}', headers={'Cache-Control': 'no-store, max-age=0'}) as resp: + if resp.status != 200: + logging.error('Invidious instance %s returned %s', i, str(resp.status)) + continue + return list(map(lambda i: (i['videoId'], i['title']), await resp.json())) + except BaseException: + logging.exception('Invidious instance %s raised exception', i) + async with session.get(f'https://www.youtube.com/feeds/videos.xml?channel_id={channel_id}&a={time.time()}', headers={'Cache-Control': 'no-store, max-age=0'}) as resp: + d = feedparser.parse(await resp.text()) + return list(map(lambda i: (i['yt_videoid'], i['title']), d['entries'])) + async def main(): await client.start(bot_token=bot_token) if storage_chat_id and storage_message_id: @@ -149,11 +164,7 @@ async def main(): while True: for i in channels: logging.info('Checking %s', i) - async with session.get(f'https://www.youtube.com/feeds/videos.xml?channel_id={i}&a={time.time()}', headers={'Cache-Control': 'no-store, max-age=0'}) as resp: - d = feedparser.parse(await resp.text()) - for e in d['entries']: - video_id = e['yt_videoid'] - video_title = e['title'] + for video_id, video_title in await get_video_list(session, i): if video_id not in seen_videos and video_id not in tmp_handled_videos: tmp_handled_videos.add(video_id) if loadmode: