Skip to content
This repository has been archived by the owner on Mar 9, 2024. It is now read-only.

Commit

Permalink
use python 3.6 typing (#33)
Browse files Browse the repository at this point in the history
all variables and functions are now typed in order to help with refactoring
  • Loading branch information
orcutt989 authored and mre committed Jan 12, 2020
1 parent 2d411b9 commit 289d1f3
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 58 deletions.
74 changes: 37 additions & 37 deletions timelapse/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,55 @@
from Foundation import NSUserDefaults


def dark_mode():
def dark_mode() -> bool:
return NSUserDefaults.standardUserDefaults().stringForKey_('AppleInterfaceStyle') == "Dark"


# Configuration
start_recording = False # Start recording on launch
encode = True # Create video after recording
screenshot_interval = 1.5 # Number of seconds between screenshots
dir_base = os.path.expanduser("~") # Base directory
dir_app = "timelapse" # Output directory
dir_pictures = "Pictures" # Place for pictures in filesystem
dir_movies = "Movies" # Place for movies in filesystem
dir_resources = "./resources/"
start_recording: bool = False # Start recording on launch
encode: bool = True # Create video after recording
screenshot_interval: float = 1.5 # Number of seconds between screenshots
dir_base: str = os.path.expanduser("~") # Base directory
dir_app: str = "timelapse" # Output directory
dir_pictures: str = "Pictures" # Place for pictures in filesystem
dir_movies: str = "Movies" # Place for movies in filesystem
dir_resources: str = "./resources/"
if dark_mode():
dir_resources += "white"
else:
dir_resources += "black"

subdir_suffix = "Session-" + time.strftime("%Y%m%d") # Name of subdirectory
image_recording = "record.gif" # App icon recording
image_idle = "stop.gif" # App icon idle
create_session_subdir = True # New screenshot directory for each session
create_movies = True # Create movies from screenshots after recording
subdir_suffix: str = "Session-" + time.strftime("%Y%m%d") # Name of subdirectory
image_recording: str = "record.gif" # App icon recording
image_idle: str = "stop.gif" # App icon idle
create_session_subdir: str = True # New screenshot directory for each session
create_movies: bool = True # Create movies from screenshots after recording
# Menu item text when recorder is running
text_recorder_running = "Stop recording"
text_recorder_idle = "Start recording" # Menu item text when recorder is idle
text_recorder_running: str = "Stop recording"
text_recorder_idle: str = "Start recording" # Menu item text when recorder is idle
# Tooltip of menu icon when not recording
tooltip_idle = "Timelapse screen recorder"
tooltip_running = "Recording | " + tooltip_idle # Tooltip when recording
tooltip_idle: str = "Timelapse screen recorder"
tooltip_running: str = "Recording | " + tooltip_idle # Tooltip when recording


###############

class Timelapse(NSObject):
""" Creates a timelapse video """

def applicationDidFinishLaunching_(self, notification):
def applicationDidFinishLaunching_(self, notification) -> None:
self.check_dependencies()

# Initialize recording
self.recording = start_recording
self.recording: bool = start_recording
self.recorder = None

# Set correct output paths
self.recorder_output_basedir = os.path.join(
self.recorder_output_basedir: str = os.path.join(
dir_base, dir_pictures, dir_app)
self.encoder_output_basedir = os.path.join(dir_base, dir_movies)
self.encoder_output_basedir: str = os.path.join(dir_base, dir_movies)

self.image_dir = self.create_dir(self.recorder_output_basedir)
self.image_dir: str = self.create_dir(self.recorder_output_basedir)

# Create a reference to the statusbar (menubar)
self.statusbar = NSStatusBar.systemStatusBar()
Expand All @@ -77,13 +77,13 @@ def applicationDidFinishLaunching_(self, notification):
self.loadIcons()
self.setStatus()

def loadIcons(self):
def loadIcons(self) -> None:
self.icon_recording = NSImage.alloc().initWithContentsOfFile_(
os.path.join(dir_resources, image_recording))
self.icon_idle = NSImage.alloc().initWithContentsOfFile_(
os.path.join(dir_resources, image_idle))

def setStatus(self):
def setStatus(self) -> None:
""" Sets the image and menu text according to recording status """
if self.recording:
self.statusitem.setImage_(self.icon_recording)
Expand All @@ -94,7 +94,7 @@ def setStatus(self):
self.recordButton.setTitle_(text_recorder_idle)
self.statusitem.setToolTip_(tooltip_idle)

def createMenu(self):
def createMenu(self) -> menu:
""" Status bar menu """
menu = NSMenu.alloc().init()
# Bind record event
Expand All @@ -107,7 +107,7 @@ def createMenu(self):
menu.addItem_(menuitem)
return menu

def startStopRecording_(self, notification):
def startStopRecording_(self, notification) -> None:
if self.recording:
self.recorder.join(timeout=screenshot_interval*2)
# Create timelapse after recording?
Expand All @@ -119,18 +119,18 @@ def startStopRecording_(self, notification):
notify("Timelapse started", "The recording has started")
self.recorder = Recorder(self.image_dir, screenshot_interval)
self.recorder.start()
self.recording = not self.recording
self.recording: bool = not self.recording
self.setStatus()

@objc.python_method
def create_dir(self, base_dir):
def create_dir(self, base_dir: str) -> str:
""" Creates a specified directory and the path to it if necessary """
if create_session_subdir:
# Create a new subdirectory
output_dir = os.path.join(base_dir, self.get_sub_dir(base_dir))
output_dir: str = os.path.join(base_dir, self.get_sub_dir(base_dir))
else:
# Don't create a subdirectory. Use base directory for output
output_dir = base_dir
output_dir: str = base_dir
# Create path if it doesn't exist
try:
print(f"Output directory: {output_dir}")
Expand All @@ -144,20 +144,20 @@ def create_dir(self, base_dir):
return output_dir

@objc.python_method
def get_sub_dir(self, base_dir):
def get_sub_dir(self, base_dir: str) -> str:
""" Returns the next nonexistend subdirectory to base_dir """
subdir_base = os.path.join(base_dir, subdir_suffix)
subdir_base: str = os.path.join(base_dir, subdir_suffix)
# Check if we can use subdir without any session id
subdir = subdir_base
subdir: str = subdir_base
# Use a session id only if subdir already exists
session_number = 0
session_number: int = 0
while os.path.exists(subdir):
# We can't use subdir. Create directory with session id
session_number += 1
subdir = subdir_base + "-" + str(session_number)
subdir: str = subdir_base + "-" + str(session_number)
return subdir

def check_dependencies(self):
def check_dependencies(self) -> None:
try:
subprocess.run(['ffmpeg'], check=True,
capture_output=True, timeout=10.0)
Expand Down
15 changes: 8 additions & 7 deletions timelapse/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import fnmatch
import shutil
from notify import notify # Shows notifications/alerts
from typing import List

not_found_msg = """
The ffmpeg command was not found;
Expand All @@ -18,30 +19,30 @@
class Encoder(Thread):
"""Create a video file from images"""

def __init__(self, input_dir, output_dir):
def __init__(self, input_dir: str, output_dir: str) -> None:
# Initialize the thread
Thread.__init__(self)

# Set config options
self.input = f"{input_dir}/%d.png"
timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
self.output = f"{output_dir}/timelapse-{timestamp}.mov"
self.input: str = f"{input_dir}/%d.png"
timestamp: str = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
self.output: str = f"{output_dir}/timelapse-{timestamp}.mov"

print("Encoder started")

def join(self, timeout=None):
def join(self, timeout=None) -> None:
""" Hard shutdown """
Thread.join(self)

def run(self):
def run(self) -> None:
"""
Now that we have screenshots of the user's desktop, we will stitch them
together using `ffmpeg` to create a movie. Each image will become
a single frame in the movie.
"""
# Call ffmpeg with settings compatible with QuickTime.
# https://superuser.com/a/820137
command = ["/usr/local/bin/ffmpeg", "-y",
command: List[str]= ["/usr/local/bin/ffmpeg", "-y",
"-framerate", "30",
"-i", self.input,
"-vf", "format=yuv420p",
Expand Down
2 changes: 1 addition & 1 deletion timelapse/notify.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os


def notify(title, text):
def notify(title: str, text: str) -> int:
os.system("""
osascript -e 'display notification "{}" with title "{}"'
""".format(text, title))
26 changes: 13 additions & 13 deletions timelapse/recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from AppKit import NSEvent, NSScreen, NSMouseInRect


def get_screen_with_mouse_index():
def get_screen_with_mouse_index() ->int:
mouseLocation = NSEvent.mouseLocation()
screens = NSScreen.screens()
for i, screen in enumerate(screens):
Expand All @@ -20,7 +20,7 @@ class Recorder(Process):
Takes a screenshot every 'interval' seconds and saves it into output_dir or a subdirectory thereof.
"""

def __init__(self, output_dir, interval=4):
def __init__(self, output_dir: str, interval: int=4) -> None:
# Initialize the thread
Process.__init__(self)

Expand All @@ -29,42 +29,42 @@ def __init__(self, output_dir, interval=4):
self._stopped = Event()

# Set config options
self.output_dir = output_dir
self.interval = interval
self.format = "png"
self.output_dir: str = output_dir
self.interval: int = interval
self.format: str = "png"

# Number of screenshots taken
self.screenshot_counter = 0
self.screenshot_counter: int = 0
print("Recorder started")

def join(self, timeout=None):
def join(self, timeout=None) -> None:
""" Stop recording """
self._stop.set()
self._stopped.wait()
print("Recorder stopped. Total recording time: " +
self.get_recording_time() + ".")
Process.join(self, timeout=timeout)

def run(self):
def run(self) -> None:
""" Periodically take a screenshots of the screen """
while not self._stop.is_set():
self.screenshot()
self._stop.wait(self.interval)

self._stopped.set()

def get_recording_time(self):
def get_recording_time(self) ->str:
return str(self.screenshot_counter * self.interval) + " seconds"

def get_filename(self):
def get_filename(self) ->str:
""" Call screencapture for mac screenshot """
filename = os.path.join(
filename: str = os.path.join(
self.output_dir, f"{self.screenshot_counter}.{self.format}")
return filename

def screenshot(self):
def screenshot(self) -> None:
""" This method uses Mac OSX screencapture from the commandline """
filename = self.get_filename()
filename: str = self.get_filename()
subprocess.run(
['screencapture', '-S', '-o', '-x', '-D',
str(get_screen_with_mouse_index() + 1), '-t', self.format, filename],
Expand Down

0 comments on commit 289d1f3

Please sign in to comment.