|  |  | @ -1,11 +1,16 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | #!/home/cronbot/anaconda3/bin/python | 
			
		
	
		
		
			
				
					
					|  |  |  | """spectur_live_view |  |  |  | """spectur_live_view | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | Open an automated selenium webdriver, and download images from Live View mode. |  |  |  | Open an automated selenium webdriver, and download images from Live View mode. | 
			
		
	
		
		
			
				
					
					|  |  |  | """ |  |  |  | """ | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | import os |  |  |  | import os | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import sys | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import glob | 
			
		
	
		
		
			
				
					
					|  |  |  | import time |  |  |  | import time | 
			
		
	
		
		
			
				
					
					|  |  |  | import pytz |  |  |  | import pytz | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import shutil | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import subprocess | 
			
		
	
		
		
			
				
					
					|  |  |  | from io import BytesIO |  |  |  | from io import BytesIO | 
			
		
	
		
		
			
				
					
					|  |  |  | from datetime import datetime |  |  |  | from datetime import datetime | 
			
		
	
		
		
			
				
					
					|  |  |  | import requests |  |  |  | import requests | 
			
		
	
	
		
		
			
				
					|  |  | @ -23,9 +28,24 @@ URL = 'https://camera.uwatchit.com.au/UWatchitWebV2/User/LiveView#' | 
			
		
	
		
		
			
				
					
					|  |  |  | OUTPUT_DIR = 'images' |  |  |  | OUTPUT_DIR = 'images' | 
			
		
	
		
		
			
				
					
					|  |  |  | TIMEZONE = 'Australia/Sydney' |  |  |  | TIMEZONE = 'Australia/Sydney' | 
			
		
	
		
		
			
				
					
					|  |  |  | WAIT_TIME = 0.1 |  |  |  | WAIT_TIME = 0.1 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | RECORDING_DURATION = 120 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | SILENT = True | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | IMAGE_MASK = 'mask/overtopping-mask.png' | 
			
		
	
		
		
			
				
					
					|  |  |  | username = '' |  |  |  | username = '' | 
			
		
	
		
		
			
				
					
					|  |  |  | password = '' |  |  |  | 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(): |  |  |  | def start_session(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     """Start automated browser session. |  |  |  |     """Start automated browser session. | 
			
		
	
	
		
		
			
				
					|  |  | @ -35,8 +55,10 @@ def start_session(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     """ |  |  |  |     """ | 
			
		
	
		
		
			
				
					
					|  |  |  |     # Start webdriver |  |  |  |     # Start webdriver | 
			
		
	
		
		
			
				
					
					|  |  |  |     options = Options() |  |  |  |     options = Options() | 
			
		
	
		
		
			
				
					
					|  |  |  |     options.headless = False |  |  |  |     options.headless = True | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     driver = webdriver.Firefox(options=options) |  |  |  |     executable_path = os.path.join(pwd, 'geckodriver') | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     driver = webdriver.Firefox(executable_path=executable_path, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                options=options) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     return driver |  |  |  |     return driver | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -75,13 +97,11 @@ def login(driver): | 
			
		
	
		
		
			
				
					
					|  |  |  |             pss.send_keys(credentials['PASSWORD']) |  |  |  |             pss.send_keys(credentials['PASSWORD']) | 
			
		
	
		
		
			
				
					
					|  |  |  |             btn.click() |  |  |  |             btn.click() | 
			
		
	
		
		
			
				
					
					|  |  |  |         except FileNotFoundError: |  |  |  |         except FileNotFoundError: | 
			
		
	
		
		
			
				
					
					|  |  |  |             # Wait for user to log in manually |  |  |  |             msg = f"""Provide spectur login details either: | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             while True: |  |  |  |             1. In the header of this script; or | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 try: |  |  |  |             2. In the file {credential_path}. | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     driver.find_element_by_id('UserName') |  |  |  |             """ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     time.sleep(1) |  |  |  |             ValueError(msg) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 except NoSuchElementException: |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     break |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     # Extract cookies |  |  |  |     # Extract cookies | 
			
		
	
		
		
			
				
					
					|  |  |  |     session = requests.Session() |  |  |  |     session = requests.Session() | 
			
		
	
	
		
		
			
				
					|  |  | @ -145,9 +165,8 @@ def get_next_image(driver, cookies): | 
			
		
	
		
		
			
				
					
					|  |  |  |     t_local = t_utc.astimezone(pytz.timezone(TIMEZONE)) |  |  |  |     t_local = t_utc.astimezone(pytz.timezone(TIMEZONE)) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     # Save image |  |  |  |     # Save image | 
			
		
	
		
		
			
				
					
					|  |  |  |     ts = t_local.strftime |  |  |  |     jpg_name = os.path.join(current_hightide_dir, 'jpg', | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     jpg_name = os.path.join(OUTPUT_DIR, ts('%Y'), ts('%Y-%m-%d'), ts('%H'), |  |  |  |                             t_local.strftime('%Y-%m-%d_%H-%M-%S%z') + '.jpg') | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             ts('%Y-%m-%d_%H-%M-%S%z') + '.jpg') |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     os.makedirs(os.path.dirname(jpg_name), exist_ok=True) |  |  |  |     os.makedirs(os.path.dirname(jpg_name), exist_ok=True) | 
			
		
	
		
		
			
				
					
					|  |  |  |     im.save(jpg_name) |  |  |  |     im.save(jpg_name) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -161,10 +180,14 @@ def get_next_image(driver, cookies): | 
			
		
	
		
		
			
				
					
					|  |  |  |     if current_time not in t: |  |  |  |     if current_time not in t: | 
			
		
	
		
		
			
				
					
					|  |  |  |         t.append(current_time) |  |  |  |         t.append(current_time) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     # Check if recording has completed | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     if t[-1] - t[0] > RECORDING_DURATION: | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         raise (StopIteration) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     # Count images collected |  |  |  |     # Count images collected | 
			
		
	
		
		
			
				
					
					|  |  |  |     n = len(t) |  |  |  |     n = len(t) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if n > 2: |  |  |  |     if (n > 2) and not SILENT: | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         # Get frames per second |  |  |  |         # Get frames per second | 
			
		
	
		
		
			
				
					
					|  |  |  |         fps = 1 / np.median(np.diff(t)) |  |  |  |         fps = 1 / np.median(np.diff(t)) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -177,7 +200,7 @@ def get_next_image(driver, cookies): | 
			
		
	
		
		
			
				
					
					|  |  |  |         print(msg, end='\r', flush=True) |  |  |  |         print(msg, end='\r', flush=True) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | def main(): |  |  |  | def get_images(): | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     # Start browser session |  |  |  |     # Start browser session | 
			
		
	
		
		
			
				
					
					|  |  |  |     driver = start_session() |  |  |  |     driver = start_session() | 
			
		
	
		
		
			
				
					
					|  |  |  |     cookies = login(driver) |  |  |  |     cookies = login(driver) | 
			
		
	
	
		
		
			
				
					|  |  | @ -195,10 +218,68 @@ def main(): | 
			
		
	
		
		
			
				
					
					|  |  |  |             # Attempt to re-login if server goes down |  |  |  |             # Attempt to re-login if server goes down | 
			
		
	
		
		
			
				
					
					|  |  |  |             time.sleep(5) |  |  |  |             time.sleep(5) | 
			
		
	
		
		
			
				
					
					|  |  |  |             cookies = login(driver) |  |  |  |             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 |  |  |  | # Create counter | 
			
		
	
		
		
			
				
					
					|  |  |  | t = [] |  |  |  | t = [] | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | # Open browser |  |  |  | # 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) | 
			
		
	
	
		
		
			
				
					|  |  | 
 |