#
# YouTube video info command
#
# Copyright © 2023 by luk3yx
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
import math, requests, random, time, traceback
# Default values so that the command still works if api.invidious.io is down
cache_expiry = -math.inf
instances = ['yewtu.be']
api_instances = ['i.psf.lt']
def fetch_instances(irc, args):
global cache_expiry, instances, api_instances
# Refresh the cache at most once per day
if time.monotonic() >= cache_expiry or not instances or not api_instances:
try:
resp = requests.get('https://api.invidious.io/instances.json')
resp.raise_for_status()
new_instances = []
new_api_instances = []
for _, info in resp.json():
if (info['type'] != 'https' or not info.get('stats') or
not info['stats'].get('openRegistrations')):
continue
new_instances.append(info['uri'])
if info['api']:
new_api_instances.append(info['uri'])
if new_instances and new_api_instances:
cache_expiry = time.monotonic() + 86400
instances = new_instances
api_instances = new_api_instances
except (requests.RequestException, IndexError, ValueError) as exc:
# This is okay, just use the cache for now
traceback.print_exc()
irc.notice(args[0], f'{exc.__class__.__name__} when fetching '
f'Invidious instance list, using cache.')
return instances, api_instances
RED = '\x0304'
GREEN = '\x0303'
def get_url_and_metadata(irc, args, video_id):
instances, api_instances = fetch_instances(irc, args)
api_url = f'{random.choice(api_instances)}'
res = requests.get(f'{api_url}/api/v1/videos/{video_id}', params={
'fields': 'title,liveNow,lengthSeconds,author,publishedText,error'
})
return f'{random.choice(instances)}/watch?v={video_id}', res.json()
def format_length(seconds):
minutes = seconds // 60
seconds %= 60
if minutes < 60:
return f'{minutes}:{seconds:02}'
return f'{minutes // 60}:{minutes % 60:02}:{seconds % 60:02}'
def num(n):
precision = 0
for prefix in ('', ' K', ' M'):
if n < 1000:
return f'\x02{n:.{precision}f}{prefix}\x02'
n /= 1000
precision = 1
return f'\x02{n:.{precision}f} B\x02'
@register_command('yt', 'youtube')
def youtube(irc, hostmask, is_admin, args):
video_url = args[1]
if 'v=' in video_url:
video_id = video_url.split('v=', 1)[1]
elif '/shorts/' in video_url:
video_id = video_url.split('/shorts/', 1)[1]
elif video_url.startswith(('youtu.be/', 'https://youtu.be/',
'http://youtu.be/')):
video_id = video_url.split('youtu.be/', 1)[1]
else:
irc.msg(args[0], 'Usage: .yt