|
|
|
@ -1,11 +1,16 @@
|
|
|
|
|
#!/home/cronbot/anaconda3/bin/python
|
|
|
|
|
"""spectur_live_view
|
|
|
|
|
|
|
|
|
|
Open an automated selenium webdriver, and download images from Live View mode.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
import glob
|
|
|
|
|
import time
|
|
|
|
|
import pytz
|
|
|
|
|
import shutil
|
|
|
|
|
import subprocess
|
|
|
|
|
from io import BytesIO
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
import requests
|
|
|
|
@ -23,9 +28,24 @@ URL = 'https://camera.uwatchit.com.au/UWatchitWebV2/User/LiveView#'
|
|
|
|
|
OUTPUT_DIR = 'images'
|
|
|
|
|
TIMEZONE = 'Australia/Sydney'
|
|
|
|
|
WAIT_TIME = 0.1
|
|
|
|
|
RECORDING_DURATION = 120
|
|
|
|
|
SILENT = True
|
|
|
|
|
IMAGE_MASK = 'mask/overtopping-mask.png'
|
|
|
|
|
username = ''
|
|
|
|
|
password = ''
|
|
|
|
|
|
|
|
|
|
# Get water level of next high tide
|
|
|
|
|
pwd = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
next_high_tide = glob.glob(pwd + '/NEXT_HIGH_TIDE=*')[0]
|
|
|
|
|
height = next_high_tide.rsplit('_', 1)[-1]
|
|
|
|
|
|
|
|
|
|
# Get current time
|
|
|
|
|
now = datetime.now()
|
|
|
|
|
year = now.strftime('%Y')
|
|
|
|
|
timestamp = now.strftime('%Y-%m-%d_%H-%M')
|
|
|
|
|
current_hightide_dir = os.path.join(pwd, OUTPUT_DIR, year,
|
|
|
|
|
f'{timestamp}_{height}')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def start_session():
|
|
|
|
|
"""Start automated browser session.
|
|
|
|
@ -35,8 +55,10 @@ def start_session():
|
|
|
|
|
"""
|
|
|
|
|
# Start webdriver
|
|
|
|
|
options = Options()
|
|
|
|
|
options.headless = False
|
|
|
|
|
driver = webdriver.Firefox(options=options)
|
|
|
|
|
options.headless = True
|
|
|
|
|
executable_path = os.path.join(pwd, 'geckodriver')
|
|
|
|
|
driver = webdriver.Firefox(executable_path=executable_path,
|
|
|
|
|
options=options)
|
|
|
|
|
|
|
|
|
|
return driver
|
|
|
|
|
|
|
|
|
@ -75,13 +97,11 @@ def login(driver):
|
|
|
|
|
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
|
|
|
|
|
msg = f"""Provide spectur login details either:
|
|
|
|
|
1. In the header of this script; or
|
|
|
|
|
2. In the file {credential_path}.
|
|
|
|
|
"""
|
|
|
|
|
ValueError(msg)
|
|
|
|
|
|
|
|
|
|
# Extract cookies
|
|
|
|
|
session = requests.Session()
|
|
|
|
@ -145,9 +165,8 @@ def get_next_image(driver, cookies):
|
|
|
|
|
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')
|
|
|
|
|
jpg_name = os.path.join(current_hightide_dir, 'jpg',
|
|
|
|
|
t_local.strftime('%Y-%m-%d_%H-%M-%S%z') + '.jpg')
|
|
|
|
|
os.makedirs(os.path.dirname(jpg_name), exist_ok=True)
|
|
|
|
|
im.save(jpg_name)
|
|
|
|
|
|
|
|
|
@ -161,10 +180,14 @@ def get_next_image(driver, cookies):
|
|
|
|
|
if current_time not in t:
|
|
|
|
|
t.append(current_time)
|
|
|
|
|
|
|
|
|
|
# Check if recording has completed
|
|
|
|
|
if t[-1] - t[0] > RECORDING_DURATION:
|
|
|
|
|
raise (StopIteration)
|
|
|
|
|
|
|
|
|
|
# Count images collected
|
|
|
|
|
n = len(t)
|
|
|
|
|
|
|
|
|
|
if n > 2:
|
|
|
|
|
if (n > 2) and not SILENT:
|
|
|
|
|
# Get frames per second
|
|
|
|
|
fps = 1 / np.median(np.diff(t))
|
|
|
|
|
|
|
|
|
@ -177,7 +200,7 @@ def get_next_image(driver, cookies):
|
|
|
|
|
print(msg, end='\r', flush=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
def get_images():
|
|
|
|
|
# Start browser session
|
|
|
|
|
driver = start_session()
|
|
|
|
|
cookies = login(driver)
|
|
|
|
@ -195,10 +218,68 @@ def main():
|
|
|
|
|
# Attempt to re-login if server goes down
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
cookies = login(driver)
|
|
|
|
|
except StopIteration:
|
|
|
|
|
# Stop when recording has completed
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_video():
|
|
|
|
|
"""Combine all images into timelapse video with ffmpeg
|
|
|
|
|
"""
|
|
|
|
|
mp4_name = os.path.join(current_hightide_dir, f'{timestamp}_{height}.mp4')
|
|
|
|
|
now.strftime('%Y-%m-%d_%H-%M')
|
|
|
|
|
command = [
|
|
|
|
|
'ffmpeg', '-hide_banner', '-loglevel', 'panic', '-pattern_type',
|
|
|
|
|
'glob', '-framerate', '10', '-i', current_hightide_dir + '/jpg/*.jpg',
|
|
|
|
|
mp4_name
|
|
|
|
|
]
|
|
|
|
|
subprocess.run(command)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_brightness(jpg_name, mask):
|
|
|
|
|
"""Get mean brighness of an image with a mask applied
|
|
|
|
|
"""
|
|
|
|
|
# Load image
|
|
|
|
|
im = Image.open(jpg_name)
|
|
|
|
|
|
|
|
|
|
# Load mask and resize of necessary
|
|
|
|
|
width, height = mask.size
|
|
|
|
|
im = im.resize((width, height), Image.LANCZOS)
|
|
|
|
|
|
|
|
|
|
# Apply mask to image
|
|
|
|
|
im.paste(mask, mask=mask)
|
|
|
|
|
|
|
|
|
|
# Convert to greyscale
|
|
|
|
|
im_grey = im.convert('LA')
|
|
|
|
|
|
|
|
|
|
# Calculate mean intensity
|
|
|
|
|
b = np.array(im_grey).mean()
|
|
|
|
|
|
|
|
|
|
return b
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Create counter
|
|
|
|
|
t = []
|
|
|
|
|
|
|
|
|
|
# Open browser
|
|
|
|
|
main()
|
|
|
|
|
get_images()
|
|
|
|
|
|
|
|
|
|
# Force browser process to end (bug with selenium)
|
|
|
|
|
subprocess.run(['killall', 'firefox'])
|
|
|
|
|
|
|
|
|
|
# Create video
|
|
|
|
|
create_video()
|
|
|
|
|
|
|
|
|
|
# Load image mask
|
|
|
|
|
mask = Image.open(os.path.join(pwd, IMAGE_MASK))
|
|
|
|
|
|
|
|
|
|
# Find brightest image for current high tide
|
|
|
|
|
jpg_names = glob.glob(current_hightide_dir + '/jpg/*')
|
|
|
|
|
b = np.zeros(len(jpg_names))
|
|
|
|
|
for i, jpg_name in enumerate(jpg_names):
|
|
|
|
|
b[i] = get_brightness(jpg_name, mask)
|
|
|
|
|
|
|
|
|
|
# Copy brightest image to root directory for current year
|
|
|
|
|
src_name = jpg_names[b.argmax()]
|
|
|
|
|
dst_name = current_hightide_dir + '.jpg'
|
|
|
|
|
shutil.copy(src_name, dst_name)
|
|
|
|
|