2023-06-26 11:26:20 +00:00
from src . fw_api import current_instance
import src . settings as settings
from pyfzf . pyfzf import FzfPrompt
from shlex import quote
from loguru import logger
import json
import time
import concurrent
import requests
fzf = FzfPrompt ( )
@logger.catch
def get_new_funkwhale_servers ( ) :
# Uses official API network.funkwhale.audio for getting new instances
public_server_api = ' https://network.funkwhale.audio/dashboards/api/tsdb/query '
now = int ( time . time ( ) )
timeback = now - 86400
request_public_servers = {
' from ' : f " { timeback } " ,
' to ' : f " { now } " ,
' queries ' : [
{
' refId ' : " A " ,
' intervalMs ' : 60000 ,
' maxDataPoints ' : 1174 ,
' datasourceId ' : 1 ,
' rawSql ' : " SELECT * FROM ( \n SELECT \n DISTINCT on (c.domain) c.domain as \" Name \" , \n c.up as \" Is up \" , \n coalesce(c.open_registrations, false) as \" Open registrations \" , \n coalesce(anonymous_can_listen, false) as \" Anonymous can listen \" , \n coalesce(c.usage_users_total, 0) as \" Total users \" , \n coalesce(c.usage_users_active_month, 0) as \" Active users (this month) \" , \n coalesce(c.software_version_major, 0)::text || ' . ' || coalesce(c.software_version_minor, 0)::text || ' . ' || coalesce(c.software_version_patch, 0)::text as \" Version \" , \n c.time as \" Last checked \" , \n d.first_seen as \" First seen \" \n FROM checks as c \n INNER JOIN domains AS d ON d.name = c.domain \n WHERE d.blocked = false AND c.up = true AND c.time > now() - INTERVAL ' 7 days ' \n AND c.anonymous_can_listen IN ( ' true ' ) \n AND c.open_registrations IN ( ' true ' , ' false ' ) \n \n ORDER BY c.domain, c.time DESC \n ) as t ORDER BY \" Active users (this month) \" DESC " ,
' format ' : " table "
}
]
}
try :
r = requests . post ( public_server_api , json = request_public_servers )
results = r . json ( )
new_instances = { }
if results :
new_instances_list = results [ ' results ' ] [ ' A ' ] [ ' tables ' ] [ 0 ] [ ' rows ' ]
for i in new_instances_list :
anonymousCanListen = i [ 1 ]
if anonymousCanListen :
new_instances [ i [ 0 ] ] = f ' { anonymousCanListen } | ? '
for i in get_new_funkwhale_servers_fediverse_observer ( ) :
new_instances [ i ] = " ? "
return new_instances
except : # If any errors then return empty list
return { }
def get_new_funkwhale_servers_fediverse_observer ( ) :
try :
graphQL_request = {
' query ' :
' { \n nodes(softwarename: \" funkwhale \" ) { \n domain \n metanodeinfo \n } \n } '
}
r = requests . post ( ' https://api.fediverse.observer/ ' ,
headers = { ' Accept-Encoding ' : ' gzip, deflate ' } ,
json = graphQL_request )
new_instances = [ ]
for i in r . json ( ) [ ' data ' ] [ ' nodes ' ] :
if i . get ( ' metanodeinfo ' ) :
auth_no_required = json . loads ( i [ ' metanodeinfo ' ] ) [ ' library ' ] [ ' anonymousCanListen ' ]
if auth_no_required and i [ ' domain ' ] :
new_instances . append ( i [ ' domain ' ] )
return new_instances
except :
return [ ]
def fetch_instances_nodeinfo_and_avalaibility ( instances ) :
extended_instances_info = { }
def request_nodeinfo ( instance ) :
return requests . get ( ' https:// ' + instance + ' /api/v1/instance/nodeinfo/2.0/ ' ,
headers = {
' Accept-Encoding ' : ' gzip, brotli, deflate ' ,
2023-08-03 08:35:37 +00:00
' User-Agent ' : ' funkwlmpv/latest-commit; +https://git.phreedom.club/localhost_frssoft/funkwlmpv ' } ,
2023-06-28 20:48:45 +00:00
timeout = 10 ) . json ( )
2023-06-26 11:26:20 +00:00
with concurrent . futures . ThreadPoolExecutor ( ) as executor : # optimally defined number of threads
res = [ executor . submit ( request_nodeinfo , instance ) for instance in instances ]
concurrent . futures . wait ( res )
for idx , v in enumerate ( instances ) :
try :
data_for_instance = res [ idx ] . result ( )
anon = data_for_instance [ ' metadata ' ] [ ' library ' ] [ ' anonymousCanListen ' ]
tracks = data_for_instance [ ' metadata ' ] [ ' library ' ] [ ' tracks ' ] [ ' total ' ]
extended_instances_info [ v ] = f ' { anon } | { tracks } '
except :
extended_instances_info [ v ] = ' fail '
return extended_instances_info
def instances_menu ( fetch_manually = False , fetch_node_info = False ) :
with open ( ' config.json ' , ' rt ' ) as f :
conf = json . loads ( f . read ( ) )
if conf . get ( ' automatic_fetch_new_instances ' ) or fetch_manually :
public_server_list_instances = get_new_funkwhale_servers ( )
new_ins_count = len ( public_server_list_instances )
else :
public_server_list_instances = { }
new_ins_count = ' Disabled '
list_instances = conf . get ( ' public_list_instances_extended ' )
if public_server_list_instances != { } :
list_instances_merge = { * * list_instances , * * public_server_list_instances }
settings . set_config ( ' public_list_instances_extended ' , list_instances_merge )
list_instances = list_instances_merge
map_in_extend_mode = ' '
if fetch_node_info :
list_instances = fetch_instances_nodeinfo_and_avalaibility ( [ instance . split ( ' | ' ) [ 0 ] . strip ( ) for instance in list_instances . keys ( ) ] )
settings . set_config ( ' public_list_instances_extended ' , list_instances )
map_in_extend_mode = ' \n map: instance | anonymousCanListen | tracks '
2023-06-28 21:02:18 +00:00
instance_menu_selector = [ ' Add new instance ' ,
' Fetch new instances ' ,
2023-06-26 11:26:20 +00:00
' Fetch nodeinfo and avalaibility ' ,
2023-06-26 19:38:57 +00:00
' Remove unreachible instances ' ,
' Shuffle ' ]
2023-06-26 11:26:20 +00:00
instance = fzf . prompt (
instance_menu_selector +
[ f ' { instance } | { info } ' for instance , info in list_instances . items ( ) ] ,
' --header= ' + quote ( f ' Select instance \n New instances: { new_ins_count } { map_in_extend_mode } ' ) )
if instance == [ ] :
return
else :
instance = instance [ 0 ] . split ( ' | ' ) [ 0 ] . strip ( )
2023-06-28 21:02:18 +00:00
if instance == ' Add new instance ' :
new = input ( ' example.com \n ' ) . strip ( )
list_instances [ new ] = ' added by user '
settings . set_config ( ' public_list_instances_extended ' , list_instances )
instance = new
2023-06-26 11:26:20 +00:00
if instance == ' Fetch new instances ' :
return instances_menu ( fetch_manually = True )
if instance == ' Fetch nodeinfo and avalaibility ' :
return instances_menu ( fetch_node_info = True )
2023-06-26 19:38:57 +00:00
if instance == ' Shuffle ' :
import random
instance = random . choice ( list ( list_instances . keys ( ) ) )
2023-06-26 11:26:20 +00:00
if instance == ' Remove unreachible instances ' :
clean_unreach = { }
for ins , info in list_instances . items ( ) :
if ' fail ' not in info . split ( ) :
clean_unreach [ ins ] = info
settings . set_config ( ' public_list_instances_extended ' , clean_unreach )
return instances_menu ( )
current_instance . select_instance ( instance )