Initial commit
commit
8c15977850
@ -0,0 +1,181 @@
|
||||
"""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 = 1
|
||||
USERNAME = ''
|
||||
PASSWORD = ''
|
||||
URL = 'https://camera.uwatchit.com.au/UWatchitWebV2/User/LiveView#'
|
||||
OUTPUT_DIR = 'images'
|
||||
TIMEZONE = 'Australia/Sydney'
|
||||
WAIT_TIME = 0.1
|
||||
|
||||
|
||||
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')
|
||||
|
||||
# Input login details
|
||||
usr.send_keys(USERNAME)
|
||||
pss.send_keys(PASSWORD)
|
||||
btn.click()
|
||||
|
||||
# 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()
|
Loading…
Reference in New Issue