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

use python 3.6 typing #33

Merged
merged 10 commits into from
Jan 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guess this needs a

from typing import List

import to work properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 👍
db3330a

"-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