"""spectur_live_view Open an automated selenium webdriver, and download images from Live View mode. """ import os import time import pytz from io import BytesIO from datetime import datetime import requests import yaml import numpy as np from PIL import Image from selenium import webdriver from selenium.webdriver.firefox.options import Options from selenium.common.exceptions import (NoSuchElementException, StaleElementReferenceException, WebDriverException) CAM_ID = 0 URL = 'https://camera.uwatchit.com.au/UWatchitWebV2/User/LiveView#' OUTPUT_DIR = 'images' TIMEZONE = 'Australia/Sydney' WAIT_TIME = 0.1 username = '' password = '' def start_session(): """Start automated browser session. Returns: selenium webdriver object """ # Start webdriver options = Options() options.headless = False driver = webdriver.Firefox(options=options) return driver def login(driver): """Log into Spectur camera portal. Args: driver: selenium webdriver object Returns: cookies from current browser session """ driver.get(URL) # Get login fields usr = driver.find_element_by_id('UserName') pss = driver.find_element_by_id('Password') btn = driver.find_element_by_id('submitlogin') if (username and password): # Login automatically if details provided usr.send_keys(username) pss.send_keys(password) btn.click() else: # Try to get credientials saved locally try: credential_path = os.path.join(os.path.expanduser('~'), '.spectur') with open(credential_path, 'r') as f: credentials = yaml.safe_load(f.read()) # Input login details usr.send_keys(credentials['USERNAME']) pss.send_keys(credentials['PASSWORD']) btn.click() except FileNotFoundError: # Wait for user to log in manually while True: try: driver.find_element_by_id('UserName') time.sleep(1) except NoSuchElementException: break # Extract cookies session = requests.Session() for c in driver.get_cookies(): session.cookies.set(c['name'], c['value']) return session.cookies def return_to_live_view(driver): """Return to live camera feed in Spectur portal. """ driver.get(URL) driver.execute_script('ChangeCam({})'.format(CAM_ID)) def get_image_timestamp(driver): """Get timestamp from image link. Args: driver: selenium webdriver object Returns: UNIX timestamp, in seconds """ im_url = driver.find_element_by_id('camimage').get_property('src') timestamp = int(im_url.rsplit('&', 1)[-1]) / 1000 return timestamp def get_next_image(driver, cookies): """Get next image. Args: driver: selenium webdriver object cookies: cookies from current browser session """ # Get timestamp t_current = get_image_timestamp(driver) # Wait until image timestamp is updated while t_current == get_image_timestamp(driver): time.sleep(WAIT_TIME) # Get image URL im_url = driver.find_element_by_id('camimage').get_property('src') # Get jpeg data, and convert to image object page = requests.get(im_url, cookies=cookies) im = Image.open(BytesIO(page.content)) # Get EXIF timestamp datestr = im._getexif()[36867] # Convert to local time t_naive = datetime.strptime(datestr, '%Y:%m:%d %H:%M:%S') t_utc = t_naive.replace(tzinfo=pytz.utc) t_local = t_utc.astimezone(pytz.timezone(TIMEZONE)) # Save image ts = t_local.strftime jpg_name = os.path.join(OUTPUT_DIR, ts('%Y'), ts('%Y-%m-%d'), ts('%H'), ts('%Y-%m-%d_%H-%M-%S%z') + '.jpg') os.makedirs(os.path.dirname(jpg_name), exist_ok=True) im.save(jpg_name) # Get battery level with open(os.path.join(OUTPUT_DIR, 'battery.txt'), 'a') as f: battery_str = driver.find_element_by_id('battery').text f.write('{}\t{}\n'.format(t_local.isoformat(), battery_str[:-1])) # Update time counter current_time = float(time.mktime(t_local.timetuple())) if current_time not in t: t.append(current_time) # Count images collected n = len(t) if n > 2: # Get frames per second fps = 1 / np.median(np.diff(t)) # Count dropped frames n_expected = fps * (t[-1] - t[0]) n_dropped = np.max([0, int(n_expected - n + 1)]) msg = '{} total images: {} fps: {} dropped frames: {}'.format( t_local.isoformat(), n, fps, n_dropped) print(msg, end='\r', flush=True) def main(): # Start browser session driver = start_session() cookies = login(driver) return_to_live_view(driver) while True: try: # Get latest image get_next_image(driver, cookies) except (NoSuchElementException, StaleElementReferenceException, WebDriverException): # Navigate to Live View return_to_live_view(driver) except ConnectionError: # Attempt to re-login if server goes down time.sleep(5) cookies = login(driver) # Create counter t = [] # Open browser main()