"""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 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 = 'unsw' password = 'Unsw123' 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) # Login automatically if details provided if (username and password): # Get login fields usr = driver.find_element_by_id('UserName') pss = driver.find_element_by_id('Password') btn = driver.find_element_by_id('submitlogin') # Input login details usr.send_keys(username) pss.send_keys(password) btn.click() else: # 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 counter t.append(time.mktime(t_local.timetuple())) # Count images collected n = len(t) if n > 1: # Get frames per second fps = 1 / np.median(np.diff(t)) # Count dropped frames n_dropped = np.max([0, int(fps * (t[-1] - t[0]) - n + 1)]) print('{} total images: {} fps: {} dropped frames: {}'.format( t_local.isoformat(), n, fps, n_dropped), 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()