diff --git a/.github/workflows/lint-and-build.yml b/.github/workflows/lint-and-build.yml index a7df7e76..6623ff54 100644 --- a/.github/workflows/lint-and-build.yml +++ b/.github/workflows/lint-and-build.yml @@ -12,6 +12,7 @@ on: branches: - main - master + - dev* paths: - "**.py" - "**.ui" diff --git a/.github/workflows/printenv.yml b/.github/workflows/printenv.yml index b9b81cbf..ed1adcbf 100644 --- a/.github/workflows/printenv.yml +++ b/.github/workflows/printenv.yml @@ -8,10 +8,10 @@ on: required: true default: false type: boolean - # push: - # branches: - # - master - # - dev + push: + branches: + - master + - dev env: GITHUB_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7b8f88e8..6aed4fc9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,12 +17,11 @@ repos: hooks: - id: pretty-format-ini args: [--autofix] - # TODO: Re-enable in dev, master doesn't have Ruff configs - # - repo: https://github.com/charliermarsh/ruff-pre-commit - # rev: "v0.0.262" # Must match requirements-dev.txt - # hooks: - # - id: ruff - # args: [--fix] + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: "v0.0.269" # Must match requirements-dev.txt + hooks: + - id: ruff + args: [--fix] - repo: https://github.com/pre-commit/mirrors-autopep8 rev: "v2.0.2" # Must match requirements-dev.txt hooks: diff --git a/README.md b/README.md index adc0d6c7..069085fe 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ This program can be used to automatically start, split, and reset your preferred - Perceptual Hash: An explanation on pHash comparison can be found [here](http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html). It is highly recommended to NOT use pHash if you use masked images, or it'll be very inaccurate. #### Capture Method + - **Windows Graphics Capture** (fast, most compatible, capped at 60fps) Only available in Windows 10.0.17134 and up. @@ -83,13 +84,14 @@ This program can be used to automatically start, split, and reset your preferred - **Force Full Content Rendering** (very slow, can affect rendering) Uses BitBlt behind the scene, but passes a special flag to PrintWindow to force rendering the entire desktop. About 10-15x slower than BitBlt based on original window size and can mess up some applications' rendering pipelines. -- **Video Capture Device** +- **Video Capture Device** Uses a Video Capture Device, like a webcam, virtual cam, or capture card. If you want to use this with OBS' Virtual Camera, use the [Virtualcam plugin](https://github.com/Avasam/obs-virtual-cam/releases) instead. #### Capture Device -Select the Video Capture Device that you wanna use if selecting the `Video Capture Device` Capture Method. +Select the Video Capture Device that you wanna use if selecting the `Video Capture Device` Capture Method. + #### Show Live Similarity @@ -216,7 +218,7 @@ The AutoSplit LiveSplit Component will directly connect AutoSplit with LiveSplit - For many games, it will be difficult to find a split image for the last split of the run. - The window of the capture region cannot be minimized. -- OBS' integrated Virtual Camera does not work and makes AutoSplit crash. +- OBS' integrated Virtual Camera does not work and makes AutoSplit crash. So it's been disabled. Use the [Virtualcam plugin](https://github.com/Avasam/obs-virtual-cam/releases) instead. ## Resources diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index fbe45ef0..1361ffac 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -28,6 +28,12 @@ Your Pull Request has to pass all checks ot be accepted. If it is still a work-i Most coding standards will be enforced by automated tooling. As time goes on, project-specific standards and "gotchas" in the frameworks we use will be listed here. +### Magic numbers + +Please avoid using magic numbers and prefer constants and enums that have a meaningful name when possible. +If a constant is shared throughout the app, it should live in `src/utils.py`. Unless it is very-specific to a module. +For image shape and channels, please use `utils.ImageShape` and `utils.ColorChannel`. + ## Testing None 😦 Please help us create test suites, we lack the time, but we really want (need!) them. diff --git a/pyproject.toml b/pyproject.toml index 8df09809..6972c868 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,8 @@ ignore = [ ### FIXME (no warnings in Ruff yet: https://github.com/charliermarsh/ruff/issues/1256): "PTH", + # Ignore until linux support + "EXE", ] [tool.ruff.per-file-ignores] @@ -104,7 +106,6 @@ max-branches = 15 # https://github.com/hhatto/autopep8#more-advanced-usage [tool.autopep8] max_line_length = 120 -recursive = true aggressive = 3 ignore = [ "E124", # Closing bracket may not match multi-line method invocation style (enforced by add-trailing-comma) diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 840db0c2..f9825aaf 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -1,10 +1,11 @@ & "$PSScriptRoot/compile_resources.ps1" $arguments = @( + "$PSScriptRoot/../src/AutoSplit.py", '--onefile', '--windowed', '--additional-hooks-dir=Pyinstaller/hooks', '--icon=res/icon.ico', '--splash=res/splash.png') -pyinstaller $arguments "$PSScriptRoot/../src/AutoSplit.py" +Start-Process -Wait -NoNewWindow pyinstaller -ArgumentList $arguments diff --git a/scripts/designer.ps1 b/scripts/designer.ps1 index 13d2450a..a6a159f6 100644 --- a/scripts/designer.ps1 +++ b/scripts/designer.ps1 @@ -1,4 +1,10 @@ -$qt6_applications_path = python3 -c 'import qt6_applications; print(qt6_applications.__path__[0])' +$qt6_applications_import = 'import qt6_applications; print(qt6_applications.__path__[0])' +$qt6_applications_path = python -c $qt6_applications_import +if ($null -eq $qt6_applications_path) { + Write-Host 'Designer not found, installing qt6_applications' + python -m pip install qt6_applications +} +$qt6_applications_path = python -c $qt6_applications_import & "$qt6_applications_path/Qt/bin/designer" ` "$PSScriptRoot/../res/design.ui" ` "$PSScriptRoot/../res/about.ui" ` diff --git a/scripts/install.ps1 b/scripts/install.ps1 index cac7c468..1a513d35 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -1,14 +1,7 @@ -# Alias python3 to python on Windows -If ($IsWindows) { - $python = (Get-Command python).Source - $python3 = "$((Get-Item $python).Directory.FullName)/python3.exe" - New-Item -ItemType SymbolicLink -Path $python3 -Target $python -ErrorAction SilentlyContinue -} - # Installing Python dependencies $dev = If ($Env:GITHUB_JOB -eq 'Build') { '' } Else { '-dev' } # Ensures installation tools are up to date. This also aliases pip to pip3 on MacOS. -python3 -m pip install wheel pip setuptools --upgrade +python -m pip install wheel pip setuptools --upgrade pip install -r "$PSScriptRoot/requirements$dev.txt" --upgrade # Don't compile resources on the Build CI job as it'll do so in build script diff --git a/scripts/requirements-dev.txt b/scripts/requirements-dev.txt index 9cd0d78b..9898efaa 100644 --- a/scripts/requirements-dev.txt +++ b/scripts/requirements-dev.txt @@ -15,12 +15,12 @@ ruff>=0.0.269 # New TODO and PYI violations # Can also be downloaded externally as a non-python package # qt6-applications # Types -types-d3dshot +types-D3DShot ; sys_platform == 'win32' types-keyboard types-Pillow types-psutil types-PyAutoGUI types-pyinstaller -types-pywin32 +types-pywin32 ; sys_platform == 'win32' types-requests types-toml diff --git a/scripts/start.ps1 b/scripts/start.ps1 index d1e8ec08..70d6fd8b 100644 --- a/scripts/start.ps1 +++ b/scripts/start.ps1 @@ -1,3 +1,3 @@ param ([string]$p1) & "$PSScriptRoot/compile_resources.ps1" -python3 "$PSScriptRoot/../src/AutoSplit.py" $p1 +python "$PSScriptRoot/../src/AutoSplit.py" $p1 diff --git a/src/AutoSplit.py b/src/AutoSplit.py index 14e19ce2..6c1a30ed 100644 --- a/src/AutoSplit.py +++ b/src/AutoSplit.py @@ -244,9 +244,9 @@ def __update_live_image_details(self, capture: cv2.Mat | None, called_from_timer # Simply clear if "live capture region" setting is off if not (self.settings_dict["live_capture_region"] and capture_region_window_label): self.live_image.clear() - return - - set_preview_image(self.live_image, capture, False) + # Set live image in UI + else: + set_preview_image(self.live_image, capture, False) def __load_start_image(self, started_by_button: bool = False, wait_for_delay: bool = True): """Not thread safe (if triggered by LiveSplit for example). Use `load_start_image_signal.emit` instead.""" @@ -423,7 +423,7 @@ def __is_current_split_out_of_range(self): or self.split_image_number > len(self.split_images_and_loop_number) - 1 def undo_split(self, navigate_image_only: bool = False): - """"Undo Split" and "Prev. Img." buttons connect to here.""" + """Undo Split" and "Prev. Img." buttons connect to here.""" # Can't undo until timer is started # or Undoing past the first image if not self.is_running \ @@ -445,7 +445,7 @@ def undo_split(self, navigate_image_only: bool = False): send_command(self, "undo") def skip_split(self, navigate_image_only: bool = False): - """"Skip Split" and "Next Img." buttons connect to here.""" + """Skip Split" and "Next Img." buttons connect to here.""" # Can't skip or split until timer is started # or Splitting/skipping when there are no images left if not self.is_running \ @@ -491,7 +491,7 @@ def start_auto_splitter(self): self.start_auto_splitter_signal.emit() def __check_for_reset_state_update_ui(self): - """Check if AutoSplit is started, if not either restart (loop splits) or update the GUI.""" + """Check if AutoSplit is started, if not then update the GUI.""" if not self.is_running: self.gui_changes_on_reset(True) return True diff --git a/src/capture_method/__init__.py b/src/capture_method/__init__.py index f74be218..c6b23b9c 100644 --- a/src/capture_method/__init__.py +++ b/src/capture_method/__init__.py @@ -134,11 +134,17 @@ class CameraInfo(): resolution: tuple[int, int] +def get_input_devices(): + """https://github.com/andreaschiavinato/python_grabber/pull/24 .""" + return cast(list[str], FilterGraph().get_input_devices()) + + def get_input_device_resolution(index: int): filter_graph = FilterGraph() try: filter_graph.add_video_input_device(index) - # This can happen since OBS 29.1 DLL blocking breaking VirtualCam + # This can happen with virtual cameras throwing errors. + # For example since OBS 29.1 updated FFMPEG breaking VirtualCam 3.0 # https://github.com/Toufool/AutoSplit/issues/238 except COMError: return None @@ -148,8 +154,7 @@ def get_input_device_resolution(index: int): async def get_all_video_capture_devices() -> list[CameraInfo]: - # TODO: Fix partially Unknown list upstream - named_video_inputs: list[str] = FilterGraph().get_input_devices() + named_video_inputs = get_input_devices() async def get_camera_info(index: int, device_name: str): backend = "" @@ -175,6 +180,7 @@ async def get_camera_info(index: int, device_name: str): if resolution is not None \ else None + # Note: Return type required https://github.com/python/typeshed/issues/2652 future = asyncio.gather( *[ get_camera_info(index, name) for index, name diff --git a/src/hotkeys.py b/src/hotkeys.py index b7c95632..b0575467 100644 --- a/src/hotkeys.py +++ b/src/hotkeys.py @@ -105,7 +105,7 @@ def __validate_keypad(expected_key: str, keyboard_event: keyboard.KeyboardEvent) NOTE: This is a workaround very specific to numpads. Windows reports different physical keys with the same scan code. For example, "Home", "Num Home" and "Num 7" are all `71`. - See: https://github.com/boppreh/keyboard/issues/171#issuecomment-390437684. + See: https://github.com/boppreh/keyboard/issues/171#issuecomment-390437684 . Since we reuse the key string we set to send to LiveSplit, we can't use fake names like "num home". We're also trying to achieve the same hotkey behaviour as LiveSplit has. @@ -153,7 +153,7 @@ def __get_key_name(keyboard_event: keyboard.KeyboardEvent): def __get_hotkey_name(names: list[str]): """ Uses keyboard.get_hotkey_name but works with non-english modifiers and keypad - See: https://github.com/boppreh/keyboard/issues/516. + See: https://github.com/boppreh/keyboard/issues/516 . """ def sorting_key(key: str): return not keyboard.is_modifier(keyboard.key_to_scan_codes(key)[0]) diff --git a/src/region_selection.py b/src/region_selection.py index 498d3aec..ed89fddb 100644 --- a/src/region_selection.py +++ b/src/region_selection.py @@ -14,7 +14,7 @@ from typing_extensions import override from win32 import win32gui from win32con import SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN -from winsdk._winrt import initialize_with_window # pylint: disable=no-name-in-module +from winsdk._winrt import initialize_with_window from winsdk.windows.foundation import AsyncStatus, IAsyncOperation from winsdk.windows.graphics.capture import GraphicsCaptureItem, GraphicsCapturePicker @@ -333,7 +333,7 @@ def mouseReleaseEvent(self, event: QtGui.QMouseEvent): class SelectRegionWidget(BaseSelectWidget): """ Widget for dragging screen region - https://github.com/harupy/snipping-tool. + Originated from https://github.com/harupy/snipping-tool . """ _right: int = 0 diff --git a/src/utils.py b/src/utils.py index 9d819e7c..82d81fe8 100644 --- a/src/utils.py +++ b/src/utils.py @@ -152,7 +152,8 @@ def fire_and_forget(func: Callable[..., Any]): """ Runs synchronous function asynchronously without waiting for a response. - Uses threads on Windows because `RuntimeError: There is no current event loop in thread 'MainThread'.` + Uses threads on Windows because ~~`RuntimeError: There is no current event loop in thread 'MainThread'.`~~ + Because maybe asyncio has issues. Unsure. See alpha.5 and https://github.com/Avasam/AutoSplit/issues/36 Uses asyncio on Linux because of a `Segmentation fault (core dumped)` """ diff --git a/typings/cv2/mat_wrapper/__init__.pyi b/typings/cv2/mat_wrapper/__init__.pyi index 15d667e9..fa91b2f9 100644 --- a/typings/cv2/mat_wrapper/__init__.pyi +++ b/typings/cv2/mat_wrapper/__init__.pyi @@ -1,5 +1,3 @@ -from __future__ import annotations - import numpy as np from typing_extensions import TypeAlias