forked from tatsumoto-ren/dotfiles
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rank-invidious-instances
executable file
·95 lines (70 loc) · 2.56 KB
/
rank-invidious-instances
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/usr/bin/python3
import asyncio
import time
from typing import NamedTuple, Collection
import httpx
class Instance(NamedTuple):
url: str
health_percent: float
active_month: int
@staticmethod
def get_active_users(d: list) -> int:
try:
return int(d[1]['stats']['usage']['users']['activeMonth'])
except (ValueError, TypeError, KeyError):
return 0
@classmethod
def get_health(cls, d: list) -> float:
try:
return float(d[1]['monitor']['30dRatio']['ratio'])
except (ValueError, TypeError, KeyError):
return 0
@staticmethod
def get_uri(d: list) -> str:
return str(d[1]['uri']).rstrip('/')
@classmethod
def from_json(cls, d: list):
return cls(
url=cls.get_uri(d),
health_percent=cls.get_health(d),
active_month=cls.get_active_users(d),
)
class InstanceAlive(NamedTuple):
instance: Instance
alive: bool
def test_video_url(instance_url: str):
return '%s/watch?v=%s' % (instance_url, "AO_UNZ13_6E")
def json_url() -> str:
return 'https://api.invidious.io/instances.json'
async def parse_instances(client: httpx.AsyncClient) -> list[Instance]:
response = await client.get(json_url())
instances = []
for entry in response.json():
instance = Instance.from_json(entry)
if instance.active_month > 1:
instances.append(instance)
return instances
async def test_instance(client: httpx.AsyncClient, instance: Instance) -> InstanceAlive:
try:
response = await client.get(test_video_url(instance.url))
except (httpx.ConnectTimeout, httpx.ReadTimeout):
return InstanceAlive(instance, False, )
else:
return InstanceAlive(instance, response.status_code == 200 and 'Invidious' in response.text, )
async def filter_alive(client: httpx.AsyncClient, instances: list[Instance]) -> list[Instance]:
result: Collection[InstanceAlive] = await asyncio.gather(
*(test_instance(client, instance) for instance in instances),
return_exceptions=True
)
return [
response.instance
for response in result
if isinstance(response, InstanceAlive) and response.alive
]
async def main():
async with httpx.AsyncClient(timeout=5) as client:
instances = await filter_alive(client, await parse_instances(client))
for instance in sorted(instances, key=lambda ins: (-ins.health_percent, ins.active_month)):
print(*instance, sep='\t')
if __name__ == '__main__':
asyncio.run(main())