Skip to content

Commit 03e38ce

Browse files
authored
Merge pull request #122 from gantasmo/release/v0.1.2
fix(questmidi + dj): bind-spam guard + high-contrast deck A/B badges (v0.1.3)
2 parents dd046f5 + 7d61787 commit 03e38ce

4 files changed

Lines changed: 32 additions & 11 deletions

File tree

backend/modules/questmidi/bridge.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from __future__ import annotations
1818

1919
import asyncio
20+
import errno
2021
import logging
2122
import os
2223
import subprocess
@@ -52,6 +53,7 @@ class _State:
5253
adb_reverse_ok: bool = False
5354
started: bool = False
5455
starting: bool = False
56+
port_in_use: bool = False
5557

5658

5759
_s = _State()
@@ -155,8 +157,8 @@ def _run_adb_reverse(port: int) -> bool:
155157
check=True,
156158
)
157159
return True
158-
except Exception as e: # noqa: BLE001
159-
log.info("questmidi: adb reverse failed: %s", e)
160+
except Exception as e: # noqa: BLE001 — expected when no headset is plugged in
161+
log.debug("questmidi: adb reverse failed: %s", e)
160162
return False
161163

162164

@@ -176,8 +178,26 @@ async def ensure_started() -> None:
176178
try:
177179
port = _port()
178180
await reattach_adb()
179-
_s.server = await asyncio.start_server(_handle_quest, "127.0.0.1", port)
181+
try:
182+
_s.server = await asyncio.start_server(_handle_quest, "127.0.0.1", port)
183+
except OSError as e:
184+
# EADDRINUSE (WSAEADDRINUSE 10048 on Windows): the port is already
185+
# bound — almost always a second theDAW instance or a --reload
186+
# leftover that still owns the listener. Treat it as started so we
187+
# don't re-attempt the bind (and re-log) on every WebSocket connect;
188+
# the existing listener relays the headset.
189+
if e.errno in (errno.EADDRINUSE, 10048):
190+
_s.started = True
191+
_s.port_in_use = True
192+
log.info(
193+
"questmidi: port %d already in use — an existing bridge "
194+
"owns it; not starting a second listener",
195+
port,
196+
)
197+
return
198+
raise
180199
_s.started = True
200+
_s.port_in_use = False
181201
log.info(
182202
"questmidi: listening on 127.0.0.1:%d (adb reverse %s)",
183203
port,
@@ -211,6 +231,7 @@ def status() -> dict:
211231
return {
212232
"started": _s.started,
213233
"port": _port(),
234+
"port_in_use": _s.port_in_use,
214235
"adb_path": _adb_path(),
215236
"adb_reverse_ok": _s.adb_reverse_ok,
216237
"quest_connected": _s.quest_writer is not None,

docs/screenshots/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"generatedAt": "2026-07-04T23:27:16.514Z",
2+
"generatedAt": "2026-07-06T08:25:22.248Z",
33
"entries": [
44
{
55
"file": "01-shell-make.png",

electron-ui/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/views/DJView.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ function EditableBpmField({
440440
* panels (hero waveforms, sampler, FX racks, Next lane, source tree, library)
441441
* host a whole component; every mixer + deck control is an individual widget the
442442
* user can relocate in Design Mode. Nothing moves until the user drags. */
443-
const DJ_LAYOUT_VERSION = 23;
443+
const DJ_LAYOUT_VERSION = 24;
444444

445445
const defaultDjLayout: SurfaceLayout = {
446446
version: DJ_LAYOUT_VERSION,
@@ -457,7 +457,7 @@ const defaultDjLayout: SurfaceLayout = {
457457
center: { id: 'center', type: 'container', axis: 'column', children: ['deckmix', 'fxrow'], fr: { deckmix: 5, fxrow: 2 } },
458458
deckmix: { id: 'deckmix', type: 'container', axis: 'row', children: ['deckAcont', 'mixer', 'deckBcont'], fr: { deckAcont: 4.17953863997903, mixer: 5.180662235484642, deckBcont: 4.439799124536327 } },
459459
// ── Deck A (pad-rows wrapped with spacer panels in cont-* containers) ──
460-
deckAcont: { id: 'deckAcont', type: 'container', axis: 'column', children: ['pdA-head', 'waveAOverview', 'cont-10-e11250c4', 'cont-A-transport', 'cont-A-stems', 'fxAP', 'perfAP'], fr: { 'pdA-head': 1.12, waveAOverview: 0.72, 'cont-10-e11250c4': 4.9, 'cont-A-transport': 1.02, 'cont-A-stems': 1.1, fxAP: 3.15, perfAP: 1.35 }, framed: true, frameTitle: 'A' },
460+
deckAcont: { id: 'deckAcont', type: 'container', axis: 'column', children: ['pdA-head', 'waveAOverview', 'cont-10-e11250c4', 'cont-A-transport', 'cont-A-stems', 'fxAP', 'perfAP'], fr: { 'pdA-head': 1.12, waveAOverview: 0.72, 'cont-10-e11250c4': 4.9, 'cont-A-transport': 1.02, 'cont-A-stems': 1.1, fxAP: 3.15, perfAP: 1.35 }, framed: true },
461461
'pdA-head': { id: 'pdA-head', type: 'panel', title: 'A', flow: 'row', widgets: ['spacer:s-2-7f6f8905', 'keylockA', 'keyA', 'bpmA', 'headerA'], widgetFr: { keylockA: 0.49871465295629847, keyA: 0.8892624085426142, bpmA: 0.8514435436029266, headerA: 2.255932370970932, 'spacer:s-2-7f6f8905': 0.8892624085426142 }, widgetJustify: { headerA: 'start' }, widgetMargins: { 'spacer:s-2-7f6f8905': { t: 0, r: 8, b: 0, l: 0 } }, mirror: false, uniform: false },
462462
'pdA-jog': { id: 'pdA-jog', type: 'panel', title: 'A · Jog', flow: 'row', widgets: ['jogA'], widgetMargins: { jogA: { t: 1, r: 4, b: 3, l: 4 } }, mirror: true },
463463
'pdA-mode': { id: 'pdA-mode', type: 'panel', title: 'A · Mode', flow: 'column', widgets: ['syncLockA', 'headCueA'], mirror: true, uniform: true },
@@ -467,7 +467,7 @@ const defaultDjLayout: SurfaceLayout = {
467467
'pdA-loop': { id: 'pdA-loop', type: 'panel', title: 'A · Loop', flow: 'row', widgets: ['loopA_0', 'loopA_1', 'loopA_2', 'loopA_3', 'loopA_4', 'loopOutA'], uniform: true, mirror: true },
468468
'pdA-perf': { id: 'pdA-perf', type: 'panel', title: 'A · Perf', flow: 'row', widgets: ['rollA_0', 'rollA_1', 'rollA_2', 'slipA', 'jumpA_0', 'jumpA_1', 'jumpA_2', 'jumpA_3'], uniform: true, mirror: true },
469469
// ── Deck B ──
470-
deckBcont: { id: 'deckBcont', type: 'container', axis: 'column', children: ['pdB-head', 'waveBOverview', 'cont-2-a0e79010', 'cont-B-transport', 'cont-B-stems', 'fxBP', 'perfBP'], fr: { 'pdB-head': 1.12, waveBOverview: 0.72, 'cont-2-a0e79010': 4.9, 'cont-B-transport': 1.02, 'cont-B-stems': 1.1, fxBP: 3.15, perfBP: 1.35 }, framed: true, frameTitle: 'B' },
470+
deckBcont: { id: 'deckBcont', type: 'container', axis: 'column', children: ['pdB-head', 'waveBOverview', 'cont-2-a0e79010', 'cont-B-transport', 'cont-B-stems', 'fxBP', 'perfBP'], fr: { 'pdB-head': 1.12, waveBOverview: 0.72, 'cont-2-a0e79010': 4.9, 'cont-B-transport': 1.02, 'cont-B-stems': 1.1, fxBP: 3.15, perfBP: 1.35 }, framed: true },
471471
waveAOverview: { id: 'waveAOverview', type: 'panel', title: 'A · Overview', flow: 'row', widgets: [], pinned: 'waveAOverview', mirror: true },
472472
waveBOverview: { id: 'waveBOverview', type: 'panel', title: 'B · Overview', flow: 'row', widgets: [], pinned: 'waveBOverview' },
473473
'pdB-head': { id: 'pdB-head', type: 'panel', title: 'B', flow: 'row', widgets: ['spacer:s-1-95993441', 'keylockB', 'keyB', 'bpmB', 'headerB'], widgetFr: { keylockB: 0.5522110739502047, keyB: 1.1040505388331472, bpmB: 1.039483463396507, headerB: 2.209123002601264, 'spacer:s-1-95993441': 0.4797473058342624 }, widgetJustify: { headerB: 'end' }, widgetMargins: { 'spacer:s-1-95993441': { t: 0, r: 0, b: 0, l: 64 } }, mirror: true, uniform: false },
@@ -2836,8 +2836,8 @@ function buildDjRegistry(p: DjRegArgs): WidgetRegistry {
28362836

28372837
reg[`header${d}`] = { id: `header${d}`, label: `Deck ${d} Info`, group: grp, kind: 'fixed', source: 'builtin', render: (_s, opts) => (
28382838
<div className={`h-full w-full flex items-center gap-1.5 px-1 overflow-hidden ${opts?.mirror ? 'flex-row-reverse' : ''}`}>
2839-
<div className="shrink-0 grid place-items-center rounded border w-7 h-7" style={{ borderColor: rgba(rgbc, 0.4), background: rgba(rgbc, 0.12) }} title={`Deck ${d}`}>
2840-
<Music2 className="w-3.5 h-3.5" style={{ color: rgb(rgbc) }} />
2839+
<div className="shrink-0 grid place-items-center rounded border w-7 h-7" style={{ borderColor: rgba(rgbc, 0.6), background: rgba(rgbc, 0.2) }} title={`Deck ${d}`}>
2840+
<span className="text-[18px] font-black leading-none text-white" style={{ textShadow: `0 0 6px ${rgba(rgbc, 0.9)}, 0 1px 2px rgba(0,0,0,0.9)` }}>{d}</span>
28412841
</div>
28422842
<div className={`min-w-0 flex-1 flex flex-col ${opts?.mirror ? 'items-end text-right' : ''}`}>
28432843
<span

0 commit comments

Comments
 (0)