14 Commits

Author SHA1 Message Date
c3b06cae51 remove on: pull_request 2026-03-19 04:00:42 +00:00
4c176cfd77 patch bump
upd ruff config
2026-03-19 03:58:44 +00:00
df023c67ea add bump task 2026-03-19 03:58:28 +00:00
8b025206b1 rename exceptions + lint fixes 2026-03-19 03:51:36 +00:00
8e8e3ce8a5 upd pdm badge 2026-02-22 11:40:08 +00:00
e565283827 upd tag pattern 2025-02-18 11:25:00 +00:00
2d194e8e67 upd build_addon workflow 2025-02-18 11:24:02 +00:00
3aab5922ab rename build command to avoid overriding built-in 2025-02-14 23:33:34 +00:00
1fb435416f run scons with pdm 2025-02-14 14:32:53 +00:00
40aaeb4c54 add ignore 2025-02-14 11:57:43 +00:00
17cdd84c51 add Taskfile
invoke tasks from pdm
2025-02-14 11:44:52 +00:00
43379f1b09 fix path in release step 2025-01-24 01:41:40 +00:00
dc9ac5ddc3 add name to download action 2025-01-24 01:31:27 +00:00
9764b9d827 upd workflow 2025-01-24 01:05:57 +00:00
10 changed files with 107 additions and 90 deletions

View File

@@ -1,40 +1,35 @@
name: build addon name: Build Addon
on: on:
push: push:
tags: ["*"] tags:
# To build on main/master branch, uncomment the following line: - 'v*.*.*'
# branches: [ main , master ]
pull_request:
branches: [ main, master ]
workflow_dispatch: workflow_dispatch:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.11']
os: [ubuntu-latest]
steps: steps:
- uses: actions/checkout@v4 - name: Checkout code
uses: actions/checkout@v4
- name: Set up PDM - name: Set up PDM
uses: pdm-project/setup-pdm@v4 uses: pdm-project/setup-pdm@v4
with: with:
python-version: ${{ matrix.python-version }} python-version: '3.11'
- name: Install dependencies - name: Install dependencies
run: | run: |
pdm sync -d -G build pdm sync -d -G build
- name: building addon - name: Build addon
run: pdm run scons run: pdm run scons
- uses: actions/upload-artifact@v3 - name: Upload build artifacts
if: success()
uses: actions/upload-artifact@v4
with: with:
name: packaged_addon name: packaged_addon
path: ./*.nvda-addon path: ./*.nvda-addon
@@ -42,17 +37,20 @@ jobs:
upload_release: upload_release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ startsWith(github.ref, 'refs/tags/') }} if: ${{ startsWith(github.ref, 'refs/tags/') }}
needs: ["build"] needs: build
steps: steps:
- uses: actions/checkout@v3 - name: Download releases files
- name: download releases files
uses: actions/download-artifact@v4.1.7 uses: actions/download-artifact@v4.1.7
with:
name: packaged_addon
- name: Display structure of downloaded files - name: Display structure of downloaded files
run: ls -R run: tree
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
files: packaged_addon/*.nvda-addon files: ./*.nvda-addon
fail_on_unmatched_files: true fail_on_unmatched_files: true
prerelease: ${{ contains(github.ref, '-') }} prerelease: ${{ contains(github.ref, '-') }}

View File

@@ -1,4 +1,4 @@
[![pdm-managed](https://img.shields.io/badge/pdm-managed-blueviolet)](https://pdm.fming.dev) [![pdm-managed](https://img.shields.io/endpoint?url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fpdm-project%2F.github%2Fbadge.json)](https://pdm-project.org)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
# NVDA Addon Voicemeeter # NVDA Addon Voicemeeter

41
Taskfile.yml Normal file
View File

@@ -0,0 +1,41 @@
version: '3'
vars:
SHELL: pwsh
tasks:
default:
desc: Build the addon
cmds:
- task: build
copy:
desc: Copy sources files to scratchpad directory
platforms: [windows]
vars:
SOURCE: addon\globalPlugins\voicemeeter
DEST: ${env:appdata}\nvda\scratchpad\globalPlugins\voicemeeter\
cmds:
- "{{.SHELL}} -Command 'robocopy {{.SOURCE}} {{.DEST}} /NDL'"
ignore_error: true
build:
desc: Build the addon
platforms: [windows]
cmds:
- '{{.SHELL}} -Command "pdm run scons"'
bump:
desc: Bump the version number
preconditions:
- sh: '{{.SHELL}} -c "if (Get-Command bump) { exit 0 } else { exit 1 }"'
msg: "The 'bump' command is not available. Run `go install github.com/onyx-and-iris/bump/cmd/bump@latest`."
cmds:
- |
{{if eq .CLI_ARGS "show"}}
{{.SHELL}} -c "bump show -f buildVars.py -p \"'addon_version': '(\d+\.\d+\.\d+)'\""
{{.SHELL}} -c "bump show -f pyproject.toml -p \"version = .(\d+\.\d+\.\d+).\""
{{else}}
{{.SHELL}} -c "bump {{.CLI_ARGS}} -w -f buildVars.py -p \"'addon_version': '(\d+\.\d+\.\d+)'\""
{{.SHELL}} -c "bump {{.CLI_ARGS}} -w -f pyproject.toml -p \"version = .(\d+\.\d+\.\d+).\""
{{end}}

View File

@@ -2,7 +2,7 @@ import ctypes as ct
from ctypes.wintypes import CHAR, FLOAT, LONG from ctypes.wintypes import CHAR, FLOAT, LONG
from .cdll import libc from .cdll import libc
from .error import VMCAPIError from .error import VMAddonCAPIError
class Binds: class Binds:
@@ -41,5 +41,5 @@ class Binds:
def call(self, fn, *args, ok=(0,)): def call(self, fn, *args, ok=(0,)):
retval = fn(*args) retval = fn(*args)
if retval not in ok: if retval not in ok:
raise VMCAPIError(fn.__name__, retval) raise VMAddonCAPIError(fn.__name__, retval)
return retval return retval

View File

@@ -1,16 +1,22 @@
import ctypes as ct import ctypes as ct
import platform import platform
import winreg
from pathlib import Path from pathlib import Path
from .error import VMError from .error import VMAddonError
try:
import winreg
except ImportError as e:
ERR_MSG = 'winreg module not found, only Windows OS supported'
raise VMAddonError(ERR_MSG) from e
# Defense against edge cases where winreg imports but we're not on Windows
if platform.system() != 'Windows':
ERR_MSG = f'Unsupported OS: {platform.system()}, only Windows OS supported'
raise VMAddonError(ERR_MSG)
BITS = 64 if ct.sizeof(ct.c_voidp) == 8 else 32 BITS = 64 if ct.sizeof(ct.c_voidp) == 8 else 32
if platform.system() != 'Windows':
raise VMError('Only Windows OS supported')
VM_KEY = 'VB:Voicemeeter {17359A74-1236-5467}' VM_KEY = 'VB:Voicemeeter {17359A74-1236-5467}'
REG_KEY = '\\'.join( REG_KEY = '\\'.join(
filter( filter(
@@ -35,12 +41,14 @@ def get_vmpath():
try: try:
vm_parent = Path(get_vmpath()).parent vm_parent = Path(get_vmpath()).parent
except FileNotFoundError as e: except FileNotFoundError as e:
raise VMError('Unable to fetch DLL path from the registry') from e ERR_MSG = 'Voicemeeter installation not found in registry'
raise VMAddonError(ERR_MSG) from e
DLL_NAME = f'VoicemeeterRemote{"64" if BITS == 64 else ""}.dll' DLL_NAME = f'VoicemeeterRemote{"64" if BITS == 64 else ""}.dll'
dll_path = vm_parent.joinpath(DLL_NAME) dll_path = vm_parent.joinpath(DLL_NAME)
if not dll_path.is_file(): if not dll_path.is_file():
raise VMError(f'Could not find {dll_path}') ERR_MSG = f'Could not find {dll_path}'
raise VMAddonError(ERR_MSG)
libc = ct.WinDLL(str(dll_path)) libc = ct.WinDLL(str(dll_path))

View File

@@ -1,9 +1,9 @@
class VMError(Exception): class VMAddonError(Exception):
"""Base voicemeeterlib exception class""" """Base voicemeeter add-on exception class"""
class VMCAPIError(VMError): class VMAddonCAPIError(VMAddonError):
"""Exception raised when the C-API returns an error code""" """Exception raised when the Voicemeeter C-API returns an error code"""
def __init__(self, fn_name, code): def __init__(self, fn_name, code):
self.fn_name = fn_name self.fn_name = fn_name

View File

@@ -1,7 +1,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum, unique from enum import Enum, unique
from .error import VMError from .error import VMAddonError
@unique @unique
@@ -78,7 +78,8 @@ def kind_factory(kind_id):
elif kind_id == 'potato': elif kind_id == 'potato':
_kind_map = PotatoMap _kind_map = PotatoMap
else: else:
raise ValueError(f'Unknown Voicemeeter kind {kind_id}') ERR_MSG = f'Unknown Voicemeeter kind {kind_id}'
raise ValueError(ERR_MSG)
return _kind_map(name=kind_id) return _kind_map(name=kind_id)
@@ -87,5 +88,5 @@ def request_kind_map(kind_id):
try: try:
KIND_obj = kind_factory(kind_id) KIND_obj = kind_factory(kind_id)
except ValueError as e: except ValueError as e:
raise VMError(str(e)) from e raise VMAddonError(str(e)) from e
return KIND_obj return KIND_obj

View File

@@ -1,25 +0,0 @@
param (
[switch]$build
)
function Copy-FilesToScratchpad {
$source = Join-Path $PSScriptRoot "addon" "globalPlugins" "voicemeeter"
$target = Join-Path $env:appdata "nvda" "scratchpad" "globalPlugins" "voicemeeter"
Robocopy $source $target | Out-Null
}
function Build-Addon {
"Building add-on" | Write-Host
scons
}
function Main {
"Copying updated files to Scratchpad" | Write-Host
Copy-FilesToScratchpad
if ($build) {
Build-Addon
}
}
if ($MyInvocation.InvocationName -ne '.') { Main }

View File

@@ -28,7 +28,7 @@ addon_info = {
The add-on requires Voicemeeter to be installed.""" The add-on requires Voicemeeter to be installed."""
), ),
# version # version
'addon_version': '1.1.0', 'addon_version': '1.1.1',
# Author(s) # Author(s)
'addon_author': 'onyx-and-iris <code@onyxandiris.online>', 'addon_author': 'onyx-and-iris <code@onyxandiris.online>',
# URL for the add-on documentation support # URL for the add-on documentation support

View File

@@ -1,27 +1,22 @@
[project] [project]
name = "nvda-addon-voicemeeter" name = "nvda-addon-voicemeeter"
version = "1.1.0" version = "1.1.1"
description = "A GUI-less NVDA Addon for Voicemeeter using the Remote API" description = "A GUI-less NVDA Addon for Voicemeeter using the Remote API"
authors = [ authors = [{ name = "Onyx and Iris", email = "code@onyxandiris.online" }]
{name = "Onyx and Iris", email = "code@onyxandiris.online"},
]
dependencies = [] dependencies = []
requires-python = "==3.11.*" requires-python = "==3.11.*"
readme = "README.md" readme = "README.md"
license = {text = "MIT"} license = { text = "MIT" }
[dependency-groups] [dependency-groups]
build = [ build = ["scons>=4.8.1", "markdown>=3.7"]
"scons>=4.8.1",
"markdown>=3.7",
]
[tool.pdm] [tool.pdm]
distribution = false distribution = false
[tool.pdm.scripts] [tool.pdm.scripts]
copy = "pwsh build.ps1" copy = "task copy"
build = "pwsh build.ps1 -build" release = "task build"
[tool.ruff] [tool.ruff]
exclude = [ exclude = [
@@ -56,9 +51,11 @@ target-version = "py311"
[tool.ruff.lint] [tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Enable flake8-errmsg (EM) warnings.
# Enable flake8-bugbear (B) warnings.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or # Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default. # McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F"] select = ["E4", "E7", "E9", "EM", "F", "B"]
ignore = [] ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided. # Allow fix for all enabled rules (when `--fix`) is provided.
@@ -100,7 +97,4 @@ docstring-code-line-length = "dynamic"
max-complexity = 10 max-complexity = 10
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"__init__.py" = [ "__init__.py" = ["E402", "F401"]
"E402",
"F401",
]