import schedule import signal import json import time import socket import logging from utils.ColoredFormatter import ColoredFormatter from bs4 import BeautifulSoup as bs from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver import ActionChains from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as ec def setupLogger(): logging_format = "[%(asctime)s] (%(levelname)s) %(module)s - %(funcName)s: %(message)s" logging.basicConfig(level=logging.INFO, format=logging_format) (logger := logging.getLogger(__name__)).setLevel(logging.INFO) logger.propagate = False (logger_handler := logging.StreamHandler()).setFormatter( ColoredFormatter(fmt=logging_format) ) logger.addHandler(logger_handler) def interrupt_handler(signum, driver: webdriver.Chrome): print(f'Handling signal {signum} ({signal.Signals(signum).name}).') schedule.clear() driver.quit() raise SystemExit def setupChromeDriver(): logging.log(logging.CRITICAL, 'Setting up Chrome driver.') chrome_options = Options() chrome_options.add_argument("--headless") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--mute-audio") chrome_options.add_argument("--window-size=1280,720") chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--disable-features=WebRtcHideLocalIpsWithMdns") chrome_options.add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'}) #chrome_options.add_extension('./qryn-webrtc-exporter.crx') driver = webdriver.Chrome(options=chrome_options) #driver = webdriver.Remote(command_executor='http://localhost:4444', options=chrome_options) return driver def saveStats(stats: dict): try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(json.dumps(stats).encode(), ('localhost', 8094)) sock.close() print(f'Sent stats: {stats}') except socket.error as e: print(f'Got socket error: {e}') def downloadStats(driver: webdriver.Chrome): html = driver.find_element(By.CLASS_NAME ,'vjs-stats-list').get_attribute('innerHTML') if html is not None: htmlBS = bs(html, 'html.parser') else: raise ValueError("html is None") stats = htmlBS.find_all('div', attrs={'style': 'display: block;'}) statsDict = { stat.div.text: stat.span.text.replace('\u21d3', 'down').replace('down/', 'down /').replace('\u21d1 ', 'up').replace('\u21d1', 'up').replace('\u00b7', '-').strip() for stat in stats } for stat in statsDict: if 'Buffer State' == stat: statsDict[stat] = statsDict[stat][1:-1].split(', ') # statsDict = { # 'userName': dict( # map( # lambda stat: ( # stat.div.text, # stat.span.text.replace('\u21d3', 'down').replace('down/', 'down /').replace('\u21d1 ', 'up').replace('\u21d1', 'up').replace('\u00b7', '-').strip() # ), stats # ) # ) # } statsDict.update({'Timestamp': time.strftime('%Y-%m-%dT%H:%M:%S%z')}) statsDict['userName'] = 'user' saveStats(statsDict) def setupStats(driver: webdriver.Chrome, url: str): actions = ActionChains(driver) wait = WebDriverWait(driver, 30, poll_frequency=0.2) driver.get(url) wait.until(ec.presence_of_element_located((By.CLASS_NAME, 'vjs-big-play-button'))) actions.click(driver.find_element(By.CLASS_NAME ,'video-js')).perform() actions.context_click(driver.find_element(By.CLASS_NAME ,'video-js')).perform() statsForNerds = driver.find_elements(By.CLASS_NAME ,'vjs-menu-item') actions.pause(2) actions.click(statsForNerds[-1]).perform() wait.until(ec.text_to_be_present_in_element((By.CLASS_NAME, 'vjs-stats-list'), 'Player')) return driver if __name__ == '__main__': setupLogger() driver = setupChromeDriver() signal.signal(signal.SIGINT, lambda signum, frame: interrupt_handler(signum, driver)) setupStats(driver, "https://tube.kobim.cloud/w/9hAbiwai4rsbw9QnPpPkCd") schedule.every(1).seconds.do(downloadStats, driver) while True: schedule.run_pending()