diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7be6cf..7e36ce1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,15 @@ on: - cron: "20 23 * * 4" jobs: + lint: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v1 + - name: Python Code Quality and Lint + uses: ricardochaves/python-lint@v1.1.0 + with: + python-root-list: "timelapse" + build: runs-on: macos-latest steps: diff --git a/.gitignore b/.gitignore index a38b404..3721e9b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ venv dist .eggs build +Timelapse.egg-info diff --git a/Makefile b/Makefile index 2f5cd5f..646dbc6 100644 --- a/Makefile +++ b/Makefile @@ -18,12 +18,16 @@ install: ## Install app dependencies .PHONY: run run: clean ## Run application in shell - poetry run timelapse + poetry run .PHONY: test test: clean ## Run pytest poetry run pytest +.PHONY: lint +lint: ## Lint project code + poetry run pylint timelapse + .PHONY: open open: ## Open project folder in Finder open . diff --git a/poetry.lock b/poetry.lock index 475b4ef..e3a30c6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,6 +6,23 @@ optional = false python-versions = "*" version = "0.17" +[[package]] +category = "dev" +description = "An abstract syntax tree for Python with inference support." +name = "astroid" +optional = false +python-versions = ">=3.5.*" +version = "2.3.3" + +[package.dependencies] +lazy-object-proxy = ">=1.4.0,<1.5.0" +six = ">=1.12,<2.0" +wrapt = ">=1.11.0,<1.12.0" + +[package.dependencies.typed-ast] +python = "<3.8" +version = ">=1.4.0,<1.5" + [[package]] category = "dev" description = "Atomic file writes." @@ -28,17 +45,6 @@ dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.int docs = ["sphinx", "zope.interface"] tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -[[package]] -category = "dev" -description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" -name = "autopep8" -optional = false -python-versions = "*" -version = "1.5" - -[package.dependencies] -pycodestyle = ">=2.5.0" - [[package]] category = "dev" description = "Cross-platform colored terminal text." @@ -64,6 +70,28 @@ zipp = ">=0.5" docs = ["sphinx", "rst.linker"] testing = ["packaging", "importlib-resources"] +[[package]] +category = "dev" +description = "A Python utility / library to sort Python imports." +name = "isort" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "4.3.21" + +[package.extras] +pipfile = ["pipreqs", "requirementslib"] +pyproject = ["toml"] +requirements = ["pipreqs", "pip-api"] +xdg_home = ["appdirs (>=1.4.0)"] + +[[package]] +category = "dev" +description = "A fast and thorough lazy object proxy." +name = "lazy-object-proxy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.4.3" + [[package]] category = "dev" description = "Mach-O header analysis and editing" @@ -75,6 +103,14 @@ version = "1.14" [package.dependencies] altgraph = ">=0.15" +[[package]] +category = "dev" +description = "McCabe checker, plugin for flake8" +name = "mccabe" +optional = false +python-versions = "*" +version = "0.6.1" + [[package]] category = "dev" description = "Python module dependency analysis tool" @@ -134,11 +170,17 @@ modulegraph = ">=0.17" [[package]] category = "dev" -description = "Python style guide checker" -name = "pycodestyle" +description = "python code static checker" +name = "pylint" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.5.0" +python-versions = ">=3.5.*" +version = "2.4.4" + +[package.dependencies] +astroid = ">=2.3.0,<2.4" +colorama = "*" +isort = ">=4.2.5,<5" +mccabe = ">=0.6,<0.7" [[package]] category = "main" @@ -278,6 +320,7 @@ version = "6.1" [[package]] category = "main" description = "Wrappers for the framework Accounts on macOS" +marker = "platform_release >= \"12.0\" or platform_release >= \"14.0\"" name = "pyobjc-framework-accounts" optional = false python-versions = ">=3.6" @@ -302,6 +345,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework AdSupport on macOS" +marker = "platform_release >= \"18.0\"" name = "pyobjc-framework-adsupport" optional = false python-versions = ">=3.6" @@ -326,6 +370,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework AppleScriptObjC on macOS" +marker = "platform_release >= \"10.0\"" name = "pyobjc-framework-applescriptobjc" optional = false python-versions = ">=3.6" @@ -376,6 +421,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework AVFoundation on macOS" +marker = "platform_release >= \"11.0\" or platform_release >= \"16.0\"" name = "pyobjc-framework-avfoundation" optional = false python-versions = ">=3.6" @@ -390,6 +436,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework AVKit on macOS" +marker = "platform_release >= \"13.0\"" name = "pyobjc-framework-avkit" optional = false python-versions = ">=3.6" @@ -403,6 +450,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework BusinessChat on macOS" +marker = "platform_release >= \"18.0\"" name = "pyobjc-framework-businesschat" optional = false python-versions = ">=3.6" @@ -415,6 +463,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework CalendarStore on macOS" +marker = "platform_release >= \"9.0\"" name = "pyobjc-framework-calendarstore" optional = false python-versions = ">=3.6" @@ -439,6 +488,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework CloudKit on macOS" +marker = "platform_release >= \"14.0\"" name = "pyobjc-framework-cloudkit" optional = false python-versions = ">=3.6" @@ -465,6 +515,7 @@ pyobjc-core = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework Collaboration on macOS" +marker = "platform_release >= \"9.0\"" name = "pyobjc-framework-collaboration" optional = false python-versions = ">=3.6" @@ -477,6 +528,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework ColorSync on Mac OS X" +marker = "platform_release >= \"17.0\"" name = "pyobjc-framework-colorsync" optional = false python-versions = ">=3.6" @@ -489,6 +541,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework Contacts on macOS" +marker = "platform_release >= \"15.0\"" name = "pyobjc-framework-contacts" optional = false python-versions = ">=3.6" @@ -501,6 +554,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework ContactsUI on macOS" +marker = "platform_release >= \"15.0\"" name = "pyobjc-framework-contactsui" optional = false python-versions = ">=3.6" @@ -539,6 +593,7 @@ pyobjc-framework-CoreAudio = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework CoreBluetooth on macOS" +marker = "platform_release >= \"14.0\"" name = "pyobjc-framework-corebluetooth" optional = false python-versions = ">=3.6" @@ -576,6 +631,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework CoreLocation on macOS" +marker = "platform_release >= \"10.0\" or platform_release >= \"13.0\" or platform_release >= \"14.0\"" name = "pyobjc-framework-corelocation" optional = false python-versions = ">=3.6" @@ -588,6 +644,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework CoreMedia on macOS" +marker = "platform_release >= \"11.0\" or platform_release >= \"16.0\" or platform_release >= \"12.0\" or platform_release >= \"19.0\"" name = "pyobjc-framework-coremedia" optional = false python-versions = ">=3.6" @@ -600,6 +657,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework CoreMediaIO on macOS" +marker = "platform_release >= \"11.0\"" name = "pyobjc-framework-coremediaio" optional = false python-versions = ">=3.6" @@ -612,6 +670,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework CoreML on macOS" +marker = "platform_release >= \"17.0\"" name = "pyobjc-framework-coreml" optional = false python-versions = ">=3.6" @@ -649,6 +708,7 @@ pyobjc-framework-FSEvents = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework CoreSpotlight on macOS" +marker = "platform_release >= \"17.0\"" name = "pyobjc-framework-corespotlight" optional = false python-versions = ">=3.6" @@ -674,6 +734,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework CoreWLAN on macOS" +marker = "platform_release >= \"10.0\"" name = "pyobjc-framework-corewlan" optional = false python-versions = ">=3.6" @@ -686,6 +747,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework CryptoTokenKit on macOS" +marker = "platform_release >= \"14.0\"" name = "pyobjc-framework-cryptotokenkit" optional = false python-versions = ">=3.6" @@ -711,6 +773,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework DictionaryServices on macOS" +marker = "platform_release >= \"9.0\"" name = "pyobjc-framework-dictionaryservices" optional = false python-versions = ">=3.6" @@ -772,6 +835,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework Accounts on macOS" +marker = "platform_release >= \"12.0\"" name = "pyobjc-framework-eventkit" optional = false python-versions = ">=3.6" @@ -809,6 +873,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework ExternalAccessory on macOS" +marker = "platform_release >= \"17.0\"" name = "pyobjc-framework-externalaccessory" optional = false python-versions = ">=3.6" @@ -847,6 +912,7 @@ pyobjc-framework-FileProvider = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework FinderSync on macOS" +marker = "platform_release >= \"14.0\"" name = "pyobjc-framework-findersync" optional = false python-versions = ">=3.6" @@ -871,6 +937,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework GameCenter on macOS" +marker = "platform_release >= \"12.0\"" name = "pyobjc-framework-gamecenter" optional = false python-versions = ">=3.6" @@ -883,6 +950,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework GameController on macOS" +marker = "platform_release >= \"13.0\"" name = "pyobjc-framework-gamecontroller" optional = false python-versions = ">=3.6" @@ -895,6 +963,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework GameKit on macOS" +marker = "platform_release >= \"12.0\"" name = "pyobjc-framework-gamekit" optional = false python-versions = ">=3.6" @@ -908,6 +977,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework GameplayKit on macOS" +marker = "platform_release >= \"15.0\"" name = "pyobjc-framework-gameplaykit" optional = false python-versions = ">=3.6" @@ -921,6 +991,7 @@ pyobjc-framework-SpriteKit = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework ImageCaptureCore on macOS" +marker = "platform_release >= \"10.0\"" name = "pyobjc-framework-imagecapturecore" optional = false python-versions = ">=3.6" @@ -933,6 +1004,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework IMServicePlugIn on macOS" +marker = "platform_release >= \"11.0\"" name = "pyobjc-framework-imserviceplugin" optional = false python-versions = ">=3.6" @@ -945,6 +1017,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework InputMethodKit on macOS" +marker = "platform_release >= \"9.0\"" name = "pyobjc-framework-inputmethodkit" optional = false python-versions = ">=3.6" @@ -969,6 +1042,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework InstantMessage on macOS" +marker = "platform_release >= \"9.0\"" name = "pyobjc-framework-instantmessage" optional = false python-versions = ">=3.6" @@ -982,6 +1056,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework Intents on macOS" +marker = "platform_release >= \"16.0\"" name = "pyobjc-framework-intents" optional = false python-versions = ">=3.6" @@ -1007,6 +1082,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework IOSurface on macOS" +marker = "platform_release >= \"10.0\"" name = "pyobjc-framework-iosurface" optional = false python-versions = ">=3.6" @@ -1019,6 +1095,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework iTunesLibrary on macOS" +marker = "platform_release >= \"10.0\"" name = "pyobjc-framework-ituneslibrary" optional = false python-versions = ">=3.6" @@ -1055,6 +1132,7 @@ pyobjc-framework-CoreServices = ">=6.1" [[package]] category = "main" description = "Wrappers for libdispatch on macOS" +marker = "platform_release >= \"12.0\"" name = "pyobjc-framework-libdispatch" optional = false python-versions = ">=3.6" @@ -1080,6 +1158,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework LocalAuthentication on macOS" +marker = "platform_release >= \"14.0\"" name = "pyobjc-framework-localauthentication" optional = false python-versions = ">=3.6" @@ -1092,6 +1171,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework MapKit on macOS" +marker = "platform_release >= \"13.0\"" name = "pyobjc-framework-mapkit" optional = false python-versions = ">=3.6" @@ -1106,6 +1186,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework MediaAccessibility on macOS" +marker = "platform_release >= \"13.0\"" name = "pyobjc-framework-mediaaccessibility" optional = false python-versions = ">=3.6" @@ -1118,6 +1199,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework MediaLibrary on macOS" +marker = "platform_release >= \"13.0\"" name = "pyobjc-framework-medialibrary" optional = false python-versions = ">=3.6" @@ -1131,6 +1213,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework MediaPlayer on macOS" +marker = "platform_release >= \"16.0\"" name = "pyobjc-framework-mediaplayer" optional = false python-versions = ">=3.6" @@ -1143,6 +1226,7 @@ pyobjc-framework-AVFoundation = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework MediaToolbox on macOS" +marker = "platform_release >= \"13.0\"" name = "pyobjc-framework-mediatoolbox" optional = false python-versions = ">=3.6" @@ -1168,6 +1252,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework MetalKit on macOS" +marker = "platform_release >= \"15.0\"" name = "pyobjc-framework-metalkit" optional = false python-versions = ">=3.6" @@ -1180,6 +1265,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework ModelIO on macOS" +marker = "platform_release >= \"15.0\"" name = "pyobjc-framework-modelio" optional = false python-versions = ">=3.6" @@ -1193,6 +1279,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework MultipeerConnectivity on macOS" +marker = "platform_release >= \"14.0\"" name = "pyobjc-framework-multipeerconnectivity" optional = false python-versions = ">=3.6" @@ -1205,6 +1292,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework NaturalLanguage on macOS" +marker = "platform_release >= \"18.0\"" name = "pyobjc-framework-naturallanguage" optional = false python-versions = ">=3.6" @@ -1217,6 +1305,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework NetFS on macOS" +marker = "platform_release >= \"10.0\"" name = "pyobjc-framework-netfs" optional = false python-versions = ">=3.6" @@ -1229,6 +1318,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework Network on macOS" +marker = "platform_release >= \"18.0\"" name = "pyobjc-framework-network" optional = false python-versions = ">=3.6" @@ -1241,6 +1331,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework NetworkExtension on macOS" +marker = "platform_release >= \"15.0\"" name = "pyobjc-framework-networkextension" optional = false python-versions = ">=3.6" @@ -1253,6 +1344,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework NotificationCenter on macOS" +marker = "platform_release >= \"14.0\"" name = "pyobjc-framework-notificationcenter" optional = false python-versions = ">=3.6" @@ -1265,6 +1357,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework OpenDirectory on macOS" +marker = "platform_release >= \"10.0\"" name = "pyobjc-framework-opendirectory" optional = false python-versions = ">=3.6" @@ -1317,6 +1410,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework Photos on macOS" +marker = "platform_release >= \"15.0\"" name = "pyobjc-framework-photos" optional = false python-versions = ">=3.6" @@ -1329,6 +1423,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework PhotosUI on macOS" +marker = "platform_release >= \"15.0\"" name = "pyobjc-framework-photosui" optional = false python-versions = ">=3.6" @@ -1353,6 +1448,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework PubSub on macOS" +marker = "platform_release >= \"9.0\"" name = "pyobjc-framework-pubsub" optional = false python-versions = ">=3.6" @@ -1378,6 +1474,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework QTKit on macOS" +marker = "platform_release >= \"9.0\" and platform_release < \"19.0\"" name = "pyobjc-framework-qtkit" optional = false python-versions = ">=3.6" @@ -1417,6 +1514,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework SafariServices on macOS" +marker = "platform_release >= \"15.0\"" name = "pyobjc-framework-safariservices" optional = false python-versions = ">=3.6" @@ -1429,6 +1527,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework SceneKit on macOS" +marker = "platform_release >= \"11.0\"" name = "pyobjc-framework-scenekit" optional = false python-versions = ">=3.6" @@ -1454,6 +1553,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework ScriptingBridge on macOS" +marker = "platform_release >= \"9.0\"" name = "pyobjc-framework-scriptingbridge" optional = false python-versions = ">=3.6" @@ -1529,6 +1629,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework ServiceManagement on macOS" +marker = "platform_release >= \"10.0\"" name = "pyobjc-framework-servicemanagement" optional = false python-versions = ">=3.6" @@ -1541,6 +1642,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework Social on macOS" +marker = "platform_release >= \"12.0\"" name = "pyobjc-framework-social" optional = false python-versions = ">=3.6" @@ -1579,6 +1681,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework SpriteKit on macOS" +marker = "platform_release >= \"13.0\" or platform_release >= \"15.0\"" name = "pyobjc-framework-spritekit" optional = false python-versions = ">=3.6" @@ -1592,6 +1695,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework StoreKit on macOS" +marker = "platform_release >= \"11.0\"" name = "pyobjc-framework-storekit" optional = false python-versions = ">=3.6" @@ -1642,6 +1746,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework UserNotifications on macOS" +marker = "platform_release >= \"18.0\"" name = "pyobjc-framework-usernotifications" optional = false python-versions = ">=3.6" @@ -1654,6 +1759,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework VideoSubscriberAccount on macOS" +marker = "platform_release >= \"18.0\"" name = "pyobjc-framework-videosubscriberaccount" optional = false python-versions = ">=3.6" @@ -1666,6 +1772,7 @@ pyobjc-framework-Cocoa = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework VideoToolbox on macOS" +marker = "platform_release >= \"12.0\"" name = "pyobjc-framework-videotoolbox" optional = false python-versions = ">=3.6" @@ -1680,6 +1787,7 @@ pyobjc-framework-Quartz = ">=6.1" [[package]] category = "main" description = "Wrappers for the framework Vision on macOS" +marker = "platform_release >= \"17.0\"" name = "pyobjc-framework-vision" optional = false python-versions = ">=3.6" @@ -1742,6 +1850,23 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" version = "1.14.0" +[[package]] +category = "dev" +description = "a fork of Python 2 and 3 ast modules with type comment support" +marker = "implementation_name == \"cpython\" and python_version < \"3.8\"" +name = "typed-ast" +optional = false +python-versions = "*" +version = "1.4.1" + +[[package]] +category = "dev" +description = "Module for decorators, wrappers and monkey patching." +name = "wrapt" +optional = false +python-versions = "*" +version = "1.11.2" + [[package]] category = "dev" description = "Backport of pathlib-compatible object wrapper for zip files" @@ -1749,14 +1874,14 @@ marker = "python_version < \"3.8\"" name = "zipp" optional = false python-versions = ">=3.6" -version = "2.1.0" +version = "3.1.0" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["jaraco.itertools"] +testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "920bae03c7b89174acc76ba876f33f64c3b125ec63d4e5a77ae041f94adcd8b7" +content-hash = "d83787f03cc4eaf16405a1ba81f5bee16475c320bf64cc75a5337992b84f3b4b" python-versions = "^3.6" [metadata.files] @@ -1764,6 +1889,10 @@ altgraph = [ {file = "altgraph-0.17-py2.py3-none-any.whl", hash = "sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe"}, {file = "altgraph-0.17.tar.gz", hash = "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa"}, ] +astroid = [ + {file = "astroid-2.3.3-py3-none-any.whl", hash = "sha256:840947ebfa8b58f318d42301cf8c0a20fd794a33b61cc4638e28e9e61ba32f42"}, + {file = "astroid-2.3.3.tar.gz", hash = "sha256:71ea07f44df9568a75d0f354c49143a4575d90645e9fead6dfb52c26a85ed13a"}, +] atomicwrites = [ {file = "atomicwrites-1.3.0-py2.py3-none-any.whl", hash = "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4"}, {file = "atomicwrites-1.3.0.tar.gz", hash = "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"}, @@ -1772,9 +1901,6 @@ attrs = [ {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, ] -autopep8 = [ - {file = "autopep8-1.5.tar.gz", hash = "sha256:0f592a0447acea0c2b0a9602be1e4e3d86db52badd2e3c84f0193bfd89fd3a43"}, -] colorama = [ {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, @@ -1783,10 +1909,41 @@ importlib-metadata = [ {file = "importlib_metadata-1.5.0-py2.py3-none-any.whl", hash = "sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b"}, {file = "importlib_metadata-1.5.0.tar.gz", hash = "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302"}, ] +isort = [ + {file = "isort-4.3.21-py2.py3-none-any.whl", hash = "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"}, + {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"}, +] +lazy-object-proxy = [ + {file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"}, + {file = "lazy_object_proxy-1.4.3-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442"}, + {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win32.whl", hash = "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4"}, + {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a"}, + {file = "lazy_object_proxy-1.4.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d"}, + {file = "lazy_object_proxy-1.4.3-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a"}, + {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win32.whl", hash = "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e"}, + {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win_amd64.whl", hash = "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357"}, + {file = "lazy_object_proxy-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50"}, + {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db"}, + {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449"}, + {file = "lazy_object_proxy-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156"}, + {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531"}, + {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb"}, + {file = "lazy_object_proxy-1.4.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08"}, + {file = "lazy_object_proxy-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383"}, + {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142"}, + {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea"}, + {file = "lazy_object_proxy-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62"}, + {file = "lazy_object_proxy-1.4.3-cp38-cp38-win32.whl", hash = "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd"}, + {file = "lazy_object_proxy-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239"}, +] macholib = [ {file = "macholib-1.14-py2.py3-none-any.whl", hash = "sha256:c500f02867515e6c60a27875b408920d18332ddf96b4035ef03beddd782d4281"}, {file = "macholib-1.14.tar.gz", hash = "sha256:0c436bc847e7b1d9bda0560351bf76d7caf930fb585a828d13608839ef42c432"}, ] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] modulegraph = [ {file = "modulegraph-0.18-py2.py3-none-any.whl", hash = "sha256:60e58a96728fa741aac2800bf89cf89a59311c5bec336bd7a146a476446eb740"}, {file = "modulegraph-0.18.tar.gz", hash = "sha256:11c03dac1368bb9e7f780b58d251a0880c30b5a14816b6f88ec5a6fe1e3e5611"}, @@ -1807,9 +1964,9 @@ py2app = [ {file = "py2app-0.20-py2.py3-none-any.whl", hash = "sha256:916f9483bf733379a3a8f3216b95cef2dec6478e5b52182daab565ac2a6fc0cb"}, {file = "py2app-0.20.tar.gz", hash = "sha256:46ce0a1551c621a976ba12b4fe4e655ab39339fa1e75784cdafb4295b269bb91"}, ] -pycodestyle = [ - {file = "pycodestyle-2.5.0-py2.py3-none-any.whl", hash = "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56"}, - {file = "pycodestyle-2.5.0.tar.gz", hash = "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"}, +pylint = [ + {file = "pylint-2.4.4-py3-none-any.whl", hash = "sha256:886e6afc935ea2590b462664b161ca9a5e40168ea99e5300935f6591ad467df4"}, + {file = "pylint-2.4.4.tar.gz", hash = "sha256:3db5468ad013380e987410a8d6956226963aed94ecb5f9d3a28acca6d9ac36cd"}, ] pyobjc = [ {file = "pyobjc-6.1-py3-none-any.whl", hash = "sha256:c78056797592fcd351c60604c5a65ad5d259afd908ee1e160364ac7728959e03"}, @@ -2417,7 +2574,33 @@ six = [ {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, ] +typed-ast = [ + {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, + {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, + {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, + {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, + {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, + {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, + {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, + {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, + {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, + {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, + {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, + {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, + {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, + {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, + {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, +] +wrapt = [ + {file = "wrapt-1.11.2.tar.gz", hash = "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1"}, +] zipp = [ - {file = "zipp-2.1.0-py3-none-any.whl", hash = "sha256:ccc94ed0909b58ffe34430ea5451f07bc0c76467d7081619a454bf5c98b89e28"}, - {file = "zipp-2.1.0.tar.gz", hash = "sha256:feae2f18633c32fc71f2de629bfb3bd3c9325cd4419642b1f1da42ee488d9b98"}, + {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, + {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, ] diff --git a/pyproject.toml b/pyproject.toml index 736e5d5..aa51d9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,106 +11,11 @@ keywords = ["screen-recorder", "timelapse", "macos"] [tool.poetry.dependencies] python = "^3.6" pyobjc = "6.1" -pyobjc-core = "6.1" -pyobjc-framework-Accounts = "6.1" -pyobjc-framework-AddressBook = "6.1" -pyobjc-framework-AdSupport = "6.1" -pyobjc-framework-AppleScriptKit = "6.1" -pyobjc-framework-AppleScriptObjC = "6.1" -pyobjc-framework-ApplicationServices = "6.1" -pyobjc-framework-Automator = "6.1" -pyobjc-framework-AVFoundation = "6.1" -pyobjc-framework-AVKit = "6.1" -pyobjc-framework-BusinessChat = "6.1" -pyobjc-framework-CalendarStore = "6.1" -pyobjc-framework-CFNetwork = "6.1" -pyobjc-framework-CloudKit = "6.1" -pyobjc-framework-Cocoa = "6.1" -pyobjc-framework-Collaboration = "6.1" -pyobjc-framework-ColorSync = "6.1" -pyobjc-framework-Contacts = "6.1" -pyobjc-framework-ContactsUI = "6.1" -pyobjc-framework-CoreAudio = "6.1" -pyobjc-framework-CoreAudioKit = "6.1" -pyobjc-framework-CoreBluetooth = "6.1" -pyobjc-framework-CoreData = "6.1" -pyobjc-framework-CoreLocation = "6.1" -pyobjc-framework-CoreMedia = "6.1" -pyobjc-framework-CoreMediaIO = "6.1" -pyobjc-framework-CoreML = "6.1" -pyobjc-framework-CoreServices = "6.1" -pyobjc-framework-CoreSpotlight = "6.1" -pyobjc-framework-CoreText = "6.1" -pyobjc-framework-CoreWLAN = "6.1" -pyobjc-framework-CryptoTokenKit = "6.1" -pyobjc-framework-DictionaryServices = "6.1" -pyobjc-framework-DiscRecording = "6.1" -pyobjc-framework-DiscRecordingUI = "6.1" -pyobjc-framework-DiskArbitration = "6.1" -pyobjc-framework-DVDPlayback = "6.1" -pyobjc-framework-EventKit = "6.1" -pyobjc-framework-ExceptionHandling = "6.1" -pyobjc-framework-ExternalAccessory = "6.1" -pyobjc-framework-FinderSync = "6.1" -pyobjc-framework-FSEvents = "6.1" -pyobjc-framework-GameCenter = "6.1" -pyobjc-framework-GameController = "6.1" -pyobjc-framework-GameKit = "6.1" -pyobjc-framework-GameplayKit = "6.1" -pyobjc-framework-ImageCaptureCore = "6.1" -pyobjc-framework-IMServicePlugIn = "6.1" -pyobjc-framework-InputMethodKit = "6.1" -pyobjc-framework-InstallerPlugins = "6.1" -pyobjc-framework-InstantMessage = "6.1" -pyobjc-framework-Intents = "6.1" -pyobjc-framework-IOSurface = "6.1" -pyobjc-framework-iTunesLibrary = "6.1" -pyobjc-framework-LatentSemanticMapping = "6.1" -pyobjc-framework-LaunchServices = "6.1" -pyobjc-framework-libdispatch = "6.1" -pyobjc-framework-LocalAuthentication = "6.1" -pyobjc-framework-MapKit = "6.1" -pyobjc-framework-MediaAccessibility = "6.1" -pyobjc-framework-MediaLibrary = "6.1" -pyobjc-framework-MediaPlayer = "6.1" -pyobjc-framework-MediaToolbox = "6.1" -pyobjc-framework-MetalKit = "6.1" -pyobjc-framework-ModelIO = "6.1" -pyobjc-framework-MultipeerConnectivity = "6.1" -pyobjc-framework-NaturalLanguage = "6.1" -pyobjc-framework-NetFS = "6.1" -pyobjc-framework-Network = "6.1" -pyobjc-framework-NetworkExtension = "6.1" -pyobjc-framework-NotificationCenter = "6.1" -pyobjc-framework-OpenDirectory = "6.1" -pyobjc-framework-OSAKit = "6.1" -pyobjc-framework-Photos = "6.1" -pyobjc-framework-PhotosUI = "6.1" -pyobjc-framework-PreferencePanes = "6.1" -pyobjc-framework-PubSub = "6.1" -pyobjc-framework-QTKit = "6.1" -pyobjc-framework-Quartz = "6.1" -pyobjc-framework-SafariServices = "6.1" -pyobjc-framework-SceneKit = "6.1" -pyobjc-framework-ScreenSaver = "6.1" -pyobjc-framework-ScriptingBridge = "6.1" -pyobjc-framework-SearchKit = "6.1" -pyobjc-framework-Security = "6.1" -pyobjc-framework-SecurityFoundation = "6.1" -pyobjc-framework-SecurityInterface = "6.1" -pyobjc-framework-ServiceManagement = "6.1" -pyobjc-framework-Social = "6.1" -pyobjc-framework-SpriteKit = "6.1" -pyobjc-framework-StoreKit = "6.1" -pyobjc-framework-SyncServices = "6.1" -pyobjc-framework-SystemConfiguration = "6.1" -pyobjc-framework-UserNotifications = "6.1" -pyobjc-framework-VideoSubscriberAccount = "6.1" -pyobjc-framework-VideoToolbox = "6.1" -pyobjc-framework-Vision = "6.1" -pyobjc-framework-WebKit = "6.1" [tool.poetry.dev-dependencies] pytest = "3.9.3" py2app = "^0.20" -autopep8 = "^1.5" +pylint = "^2.4.4" + +[tool.poetry.scripts] +run = "timelapse.__main__:main" \ No newline at end of file diff --git a/timelapse/__init.py__ b/timelapse/__init.py__ index 82d7ba4..e69de29 100644 --- a/timelapse/__init.py__ +++ b/timelapse/__init.py__ @@ -1 +0,0 @@ -#!/usr/bin/env python -O diff --git a/timelapse/__main__.py b/timelapse/__main__.py index dae82dc..97d807d 100644 --- a/timelapse/__main__.py +++ b/timelapse/__main__.py @@ -1,18 +1,35 @@ +""" +The main entrypoint of the application, +which initializes the recorder and the encoder. +""" + import os +import sys import time import subprocess from pathlib import Path +from AppKit import NSObject, NSMenu, NSApplication, NSStatusBar, \ + NSMenuItem, NSImage, NSVariableStatusItemLength, objc from PyObjCTools import AppHelper -from AppKit import * - -from notify import notify # Shows notifications/alerts -from encoder import Encoder, not_found_msg # Creates timelapse video -from recorder import Recorder # Takes screenshots from Foundation import NSUserDefaults +from timelapse.encoder import Encoder # Creates timelapse video +from timelapse.recorder import Recorder # Takes screenshots +from timelapse.notify import notify # Shows notifications/alerts + +NOT_FOUND_MSG = """ +The ffmpeg command was not found; +ffmpeg is used by this script to make a video file from a set of pngs. +It is typically not installed by default distros , but it is widely available. +On macOS, try running `brew install ffmpeg`. +""" + def dark_mode() -> bool: + """ + Check if the user enabled Dark Mode + """ return NSUserDefaults.standardUserDefaults().stringForKey_('AppleInterfaceStyle') == "Dark" @@ -20,7 +37,7 @@ def dark_mode() -> bool: 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(Path.home()) # Base directory +DIR_BASE = str(Path.home()) # 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 @@ -56,8 +73,9 @@ def applicationDidFinishLaunching_(self, notification) -> None: # Set correct output paths self.recorder_output_basedir: str = os.path.join( - dir_base, dir_pictures, dir_app) - self.encoder_output_basedir: str = os.path.join(dir_base, dir_movies) + DIR_BASE, dir_pictures, dir_app) + self.encoder_output_basedir: str = os.path.join(DIR_BASE, dir_movies) + self.image_dir: str = self.create_dir(self.recorder_output_basedir) # Create a reference to the statusbar (menubar) self.statusbar = NSStatusBar.systemStatusBar() @@ -72,33 +90,33 @@ def applicationDidFinishLaunching_(self, notification) -> None: self.statusitem.setMenu_(self.menu) # Load icons and show them in the statusbar - self.loadIcons() - self.setStatus() + self.load_icons() + self.set_status() - def loadIcons(self) -> None: + def load_icons(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) -> None: + def set_status(self) -> None: """ Sets the image and menu text according to recording status """ if self.recording: self.statusitem.setImage_(self.icon_recording) - self.recordButton.setTitle_(text_recorder_running) + self.record_button.setTitle_(text_recorder_running) self.statusitem.setToolTip_(tooltip_running) else: self.statusitem.setImage_(self.icon_idle) - self.recordButton.setTitle_(text_recorder_idle) + self.record_button.setTitle_(text_recorder_idle) self.statusitem.setToolTip_(tooltip_idle) def createMenu(self) -> NSMenu: """ Status bar menu """ menu = NSMenu.alloc().init() # Bind record event - self.recordButton = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_( + self.record_button = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_( text_recorder_idle, 'startStopRecording:', '') - menu.addItem_(self.recordButton) + menu.addItem_(self.record_button) # Quit event menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_( 'Quit', 'terminate:', '') @@ -114,12 +132,11 @@ def startStopRecording_(self, notification) -> None: self.image_dir, self.encoder_output_basedir) self.encoder.start() else: - self.image_dir: str = self.create_dir(self.recorder_output_basedir) self.recorder = Recorder(self.image_dir, screenshot_interval) self.recorder.start() notify("Timelapse started", "The recording has started") self.recording: bool = not self.recording - self.setStatus() + self.set_status() @objc.python_method def create_dir(self, base_dir: str) -> str: @@ -131,9 +148,9 @@ def create_dir(self, base_dir: str) -> str: try: print(f"Output directory: {output_dir}") os.makedirs(output_dir) - except OSError as e: - notify("Timelapse error", f"Error while creating directory: {e}") - exit() + except OSError as os_error: + notify("Timelapse error", f"Error while creating directory: {os_error}") + sys.exit() return output_dir @objc.python_method @@ -153,11 +170,15 @@ def check_dependencies(self) -> None: # This is a quick and dirty check; it leaves some spurious output # for the user to puzzle over. except OSError: - print(not_found_msg) + print(NOT_FOUND_MSG) + notify("Timelapse", "ERROR: ffmpeg not found") + sys.exit() - -if __name__ == "__main__": +def main(): app = NSApplication.sharedApplication() delegate = Timelapse.alloc().init() app.setDelegate_(delegate) AppHelper.runEventLoop() + +if __name__ == "__main__": + main() diff --git a/timelapse/encoder.py b/timelapse/encoder.py index 020f3dc..2b3a304 100644 --- a/timelapse/encoder.py +++ b/timelapse/encoder.py @@ -1,19 +1,12 @@ +""" +Deals with encoding the screenshots to video using ffmpeg. +""" + from threading import Thread # Encoder is a thread import subprocess from datetime import datetime -import os -import glob -import fnmatch -import shutil -from notify import notify # Shows notifications/alerts from typing import List - -not_found_msg = """ -The ffmpeg command was not found; -ffmpeg is used by this script to make a video file from a set of pngs. -It is typically not installed by default distros , but it is widely available. -On macOS, try running `brew install ffmpeg`. -""" +from timelapse.notify import notify # Shows notifications/alerts class Encoder(Thread): @@ -48,16 +41,11 @@ def run(self) -> None: "-vf", "format=yuv420p", "-vcodec", "h264", self.output] + notify("Timelapse", f"Creating timelapse. This might take a while") + print(' '.join(command)) try: - notify("Timelapse", f"Creating timelapse. This might take a while") - print(' '.join(command)) - try: - completed = subprocess.run( - command, capture_output=True, check=True) - except subprocess.CalledProcessError as e: - notify("Timelapse: ffmpeg not found.", e.stderr.decode('utf-8')) - else: - notify("Timelapse", f"Movie saved to `{self.output}`") - except Exception as e: - print("Main exception", e) - notify("Timelapse Error", e) + subprocess.run(command, capture_output=True, check=True) + except subprocess.CalledProcessError as ffmpeg_error: + notify("Timelapse: ffmpeg not found.", ffmpeg_error.stderr.decode('utf-8')) + else: + notify("Timelapse", f"Movie saved to `{self.output}`") diff --git a/timelapse/notify.py b/timelapse/notify.py index ebf1446..565444b 100644 --- a/timelapse/notify.py +++ b/timelapse/notify.py @@ -1,6 +1,13 @@ -from subprocess import run +""" +Functionality to show notifications on macOS +""" + +import subprocess def notify(title: str, text: str) -> int: + """ + Show a macOS notification using osascript + """ script = 'display notification "{}" with title "{}"'.format(text, title) - return run(['osascript', '-e', script]).returncode + return subprocess.run(['osascript', '-e', script], check=True).returncode diff --git a/timelapse/recorder.py b/timelapse/recorder.py index ce221ef..02e5239 100644 --- a/timelapse/recorder.py +++ b/timelapse/recorder.py @@ -1,26 +1,32 @@ +""" +Recorder is responsible for taking regular (1 second) screenshots of the +screen where the mouse is located. +""" + import os import subprocess # Taking screenshot -import datetime # Filename from multiprocessing import Process, Event -from PyObjCTools import AppHelper from AppKit import NSEvent, NSScreen, NSMouseInRect - def get_screen_with_mouse_index() ->int: - mouseLocation = NSEvent.mouseLocation() + """ + Get the ID for the screen where the mouse is currently on. + """ + mouse_location = NSEvent.mouseLocation() screens = NSScreen.screens() for i, screen in enumerate(screens): - if NSMouseInRect(mouseLocation, screen.frame(), False): + if NSMouseInRect(mouse_location, screen.frame(), False): return i return 0 class Recorder(Process): """ - Takes a screenshot every 'interval' seconds and saves it into output_dir or a subdirectory thereof. + Takes a screenshot every 'interval' seconds and saves it into output_dir or + a subdirectory thereof. """ - def __init__(self, output_dir: str, interval: int=4) -> None: + def __init__(self, output_dir: str, interval: int = 4) -> None: # Initialize the thread Process.__init__(self) @@ -54,6 +60,7 @@ def run(self) -> None: self._stopped.set() def get_recording_time(self) ->str: + """ Get the total recording time as a human-readable string """ return str(self.screenshot_counter * self.interval) + " seconds" def get_filename(self) ->str: @@ -67,6 +74,6 @@ def screenshot(self) -> None: filename: str = self.get_filename() subprocess.run( ['screencapture', '-S', '-o', '-x', '-D', - str(get_screen_with_mouse_index() + 1), '-t', self.format, filename], + str(get_screen_with_mouse_index() + 1), '-t', self.format, filename], check=True) self.screenshot_counter += 1