Compare commits

...

10 commits

Author SHA1 Message Date
Zac Gaetano
ba66bd743a Remove multi-camera switcher, add Wild Dragon logo watermark
Single-camera setup: removed CAM1-8 switcher nav, switchCamera()
function, and all .camBtn styles. ci is now permanently 0.

Wild Dragon SVG logo added to resources/ and rendered as a centered
background watermark (brightness(0) invert(1), 3.5% opacity) so it
reads as a faint white ghost on the dark theme without interfering
with any controls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 21:55:02 -04:00
Zac Gaetano
e423bd4bf6 Redesign UI and add color science / video mode controls
- Full UI rewrite: dark tabbed layout (Exposure, Color Science, Color
  Correction, Lens, System) replacing cramped two-panel layout
- Exposure tab: large pill controls for ISO/Shutter/ND/Gain/WB with
  ◀ ▶ nudge buttons, WB presets, AE mode row
- Color Science tab (new): gamma curve selector, color gamut selector,
  quick presets (BRAW Film/Video/Ext.Video/Rec.709), video format
  (codec + frame rate) controls via /system/format
- Color Correction tab: cleaner LGGO rows with per-channel LRGB labels,
  contrast and hue/sat/luma-contribution sliders
- Lens tab: large vertical sliders for Focus/Iris/Zoom with AF button
- System tab: connection, presets, manual API, footer links
- BMDevice.js: added setColorScience(gamut, gamma) and setVideoFormat()
- Always-visible top bar: recording pulse animation, transport controls,
  timecode, format display, camera switcher

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 21:52:20 -04:00
DylanSpeiser
6fcd7f0317
Update README.md 2025-02-24 10:38:59 -05:00
DylanSpeiser
53fd2ebd3d
Merge pull request #5 from jullrich/StoringCameraHostname
adding javascript to store hostname in local storage between restart
2025-02-24 10:37:57 -05:00
Johannes B. Ullrich
47ddf3552f
adding javascript to store hostname in local storage between restart 2025-02-23 10:55:16 -05:00
DylanSpeiser
8eb38b6c40 1.4.2
Footer fix
2024-10-30 15:16:59 -04:00
DylanSpeiser
5ad7289977 1.4.1
Big fix
2024-10-30 15:06:52 -04:00
DylanSpeiser
47de0198a5
Merge pull request #1 from LukasW1337/auto-white-balance
Adds a Auto White Balance button
2024-10-30 14:48:21 -04:00
DylanSpeiser
a36b06a4a5 Squashed commit of the following:
commit 43fc71c0ca
Author: DylanSpeiser <bluedisney16@gmail.com>
Date:   Wed Oct 30 14:46:11 2024 -0400

    v1.4

    Incorporate Auto WB button, improve page layout on small screens

commit 9c0a8765ce
Author: LukasW1337 <wiinholtt@gmail.com>
Date:   Wed Aug 28 16:30:41 2024 +0200

    Adds a Auto White Balance button
2024-10-30 14:46:42 -04:00
DylanSpeiser
43fc71c0ca v1.4
Incorporate Auto WB button, improve page layout on small screens
2024-10-30 14:46:11 -04:00
8 changed files with 1910 additions and 1252 deletions

View file

@ -245,6 +245,18 @@ class BMCamera extends BMDevice {
doAutoWhitebalance() {
this.PUTdata("/video/whiteBalance/doAuto");
}
// gamut and gamma can each be null to leave the other unchanged
setColorScience(gamut, gamma) {
const data = {};
if (gamut !== null && gamut !== undefined) data.gamut = gamut;
if (gamma !== null && gamma !== undefined) data.gamma = gamma;
if (Object.keys(data).length) this.PUTdata("/video/colorScience", data);
}
setVideoFormat(data) {
this.PUTdata("/system/format", data);
}
}
/* Helper Functions */

22
PRODUCT.md Normal file
View file

@ -0,0 +1,22 @@
# BM Camera Control WebUI
## Product Purpose
A browser-based remote control panel for Blackmagic Design cameras via the official REST API over a local network. Used on set to adjust camera parameters without touching the camera body.
## Users
- Camera operators: need one-tap access to recording state and exposure controls
- DITs (Digital Imaging Technicians): need color science selection, color correction, and format settings
- Directors / ACs: need timecode and recording status visible at a glance
- All users may be working in dim environments (video village, studio) and on tablets
## Register
product
## Tone
Clinical and fast. Every control earns its place. No decorative chrome. Quiet when nothing is happening; clear when something requires attention (especially recording state).
## Anti-references
- Neon SaaS dashboards
- Consumer camera apps (bright white, rounded, playful)
- Generic Bootstrap-dark or shadcn defaults
- Glassmorphism decoration

View file

@ -22,7 +22,7 @@ If your camera does not have an ethernet port, use a USB-C to ethernet adapter.
![BM Camera Setup](screenshots/BMCameraSetup2.png)
> If you're using the GitHub Pages site, the API must be accessed with HTTPS rather than HTTP. You can enable this on the camera in **Blackmagic Camera Setup** by clicking the "Generate Certificate" button.
> If you're using the GitHub Pages site, the API must be accessed with HTTPS rather than HTTP. You can enable this on the camera in **Blackmagic Camera Setup** by clicking the "Generate Certificate" button. You might have to convince your browser to trust the certificate by navigating to the API endpoint, overriding your browser's "this page isn't secure!" warnings, and then going back to the WebUI and connecting with HTTPS.
## Launching the App
The app is a self-contained, offline web page. (No installation, dependencies, or servers to worry about!) Simply open the `index.html` file in your browser of choice, enter the hostname of your camera, and press "Connect".
@ -49,6 +49,9 @@ Because the app is just a web page, you can open it in multiple browser windows
<img src="screenshots/WebUI2.png" width=30%>
### Tablet Screens
If you're having trouble seeing the bottom of the controls because of the footer (which can happen on tablets), just tap on the footer bar to hide it. Tap on the header bar to bring the footer back.
### Data Synchronization
The app uses WebSockets to keep itself updated with the latest info from the camera. If something has gone wrong, refresh the page.
@ -142,6 +145,7 @@ If you're having trouble and don't know why, check the browser console.
- Save / download preset files to/from the camera
### For License and Copyright details, See `LICENSE.txt`
(c) 2024 Dylan Speiser
First made in 2024 by Dylan Speiser
Big thank you to all contributors!
<br>
Licensed under the GNU General Public License

View file

@ -1,349 +1,552 @@
<!-- (c) 2024 Dylan Speiser -->
<!DOCTYPE html>
<html>
<head>
<!-- Page title and metadata -->
<title>Camera Control WebUI for Blackmagic Cameras</title>
<meta charset="UTF-8">
<meta name="description" content="JS-based web interface for controlling Blackmagic Design cameras via the official REST API">
<meta name="author" content="Dylan Speiser">
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BM Camera Control</title>
<link rel="stylesheet" href="style.css">
</head>
<body onload="bodyOnLoad()">
<script src="BMDevice.js"></script>
<script src="web-ui.js"></script>
<!-- Linking the stylesheet -->
<link rel="stylesheet" href="style.css">
</head>
<body onload="bodyOnLoad()">
<!-- JavaScript Linking -->
<script src="BMDevice.js"></script>
<script src="web-ui.js"></script>
<!-- Background logo watermark -->
<div id="bgLogo" aria-hidden="true">
<img src="resources/wilddragon-logo.svg" alt="">
</div>
<!------ Page Content ------>
<!-- Header Div -->
<div class="flexContainerH" id="headerContainer">
<h1>Camera Control WebUI for Blackmagic Cameras</h1>
<!-- Top Bar -->
<header id="topBar">
<div id="camInfo">
<span id="cameraName">NOT CONNECTED</span>
<span id="timecodeLabel" class="timecode">--:--:--:--</span>
</div>
<!-- Camera Select Bar -->
<div class="flexContainerH" id="cameraSelectContainer">
<span class="cameraSwitchLabel selectedCam"><a href="#" onclick="switchCamera(0)">CAM1</a></span>
<span class="camSelectSeparator">|</span>
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(1)">CAM2</a></span>
<span class="camSelectSeparator">|</span>
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(2)">CAM3</a></span>
<span class="camSelectSeparator">|</span>
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(3)">CAM4</a></span>
<span class="camSelectSeparator">|</span>
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(4)">CAM5</a></span>
<span class="camSelectSeparator">|</span>
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(5)">CAM6</a></span>
<span class="camSelectSeparator">|</span>
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(6)">CAM7</a></span>
<span class="camSelectSeparator">|</span>
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(7)">CAM8</a></span>
<div id="formatDisplay">
<span id="formatCodec"></span>
<span id="formatResolution"></span>
<span id="formatFPS"></span>
</div>
<!-- Camera Controls Box -->
<div class="flexContainerH" id="allCamerasContainer">
<div class="flexContainerV" id="cameraControlsContainer">
<div class="flexContainerH" id="cameraControlHeadContainer">
<h2 id="cameraNumberLabel">CAM1</h2>
</div>
<div id="transportControls">
<button class="iconBtn" onclick="loopHandler('Loop')" title="Loop" id="loopButton">&#8635;</button>
<button class="iconBtn" onclick="loopHandler('Single Clip')" title="Single Clip" id="singleClipButton">S</button>
<button class="iconBtn" onclick="cameras[ci].seek(false)" title="Previous">&#9204;</button>
<button class="iconBtn" onclick="cameras[ci].play()" title="Play">&#9654;</button>
<button class="iconBtn" onclick="cameras[ci].stop()" title="Stop">&#9209;</button>
<button class="iconBtn" onclick="cameras[ci].seek(true)" title="Next">&#9205;</button>
</div>
<div class="flexContainerH" id="cameraControlColorCorrectionContainer">
<!-- <div class="flexContainerH" id="cameraControlLGGTabs">
<a href="#" class="ccTabLabel selectedTab" onclick="">Lift</a>
<a href="#" class="ccTabLabel" onclick="">Gamma</a>
<a href="#" class="ccTabLabel" onclick="">Gain</a>
</div> -->
<span style="margin-top: 0.5em;">Lift</span>
<div class="flexContainerH" id="cameraControlColorCorrectionBottomContainer">
<button class="CCResetButton circleButton" onclick="resetCC(0)" title="Reset Lift">&#10227</button>
<div class="flexContainerH" id="cameraControlColorCorrectionNumbersContainer">
<span contenteditable="plaintext-only" style="text-decoration-color: #dbdbdb;" class="CClumaLabel" onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #e64b3d;" class="CCredLabel" onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #00a841;" class="CCgreenLabel" onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #2a78c8;" class="CCblueLabel" onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
</div>
<button id="CCHamburgerButton" class="circleButton" onclick="setCCFromUI(0)" title="Set Lift">&#10138</button>
</div>
<button id="recordButton" onclick="cameras[ci].toggleRecord()" title="Toggle Record">
<span id="recDot"></span>
<span id="recLabel">REC</span>
</button>
</header>
<span>Gamma</span>
<div class="flexContainerH" id="cameraControlColorCorrectionBottomContainer">
<button class="CCResetButton circleButton" onclick="resetCC(1)" title="Reset Gamma">&#10227</button>
<div class="flexContainerH" id="cameraControlColorCorrectionNumbersContainer">
<span contenteditable="plaintext-only" style="text-decoration-color: #dbdbdb;" class="CClumaLabel" onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #e64b3d;" class="CCredLabel" onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #00a841;" class="CCgreenLabel" onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #2a78c8;" class="CCblueLabel" onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
</div>
<button id="CCHamburgerButton" class="circleButton" onclick="setCCFromUI(1)" title="Set Gamma">&#10138</button>
</div>
<!-- Tab Bar -->
<nav id="tabBar">
<button class="tab active" onclick="switchTab('exposure')" id="tab-btn-exposure">Exposure</button>
<button class="tab" onclick="switchTab('colorscience')" id="tab-btn-colorscience">Color Science</button>
<button class="tab" onclick="switchTab('colorcorrection')" id="tab-btn-colorcorrection">Color Correction</button>
<button class="tab" onclick="switchTab('lens')" id="tab-btn-lens">Lens</button>
<button class="tab" onclick="switchTab('system')" id="tab-btn-system">System</button>
</nav>
<span>Gain</span>
<div class="flexContainerH" id="cameraControlColorCorrectionBottomContainer">
<button class="CCResetButton circleButton" onclick="resetCC(2)" title="Reset Gain">&#10227</button>
<div class="flexContainerH" id="cameraControlColorCorrectionNumbersContainer">
<span contenteditable="plaintext-only" style="text-decoration-color: #dbdbdb;" class="CClumaLabel" onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #e64b3d;" class="CCredLabel" onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #00a841;" class="CCgreenLabel" onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #2a78c8;" class="CCblueLabel" onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">0.00</span>
</div>
<button id="CCHamburgerButton" class="circleButton" onclick="setCCFromUI(2)" title="Set Gain">&#10138</button>
</div>
<!-- ======== MAIN CONTENT ======== -->
<main id="mainContent">
<span>Offset</span>
<div class="flexContainerH" id="cameraControlColorCorrectionBottomContainer">
<button class="CCResetButton circleButton" onclick="resetCC(3)" title="Reset Offset">&#10227</button>
<div class="flexContainerH" id="cameraControlColorCorrectionNumbersContainer">
<span contenteditable="plaintext-only" style="text-decoration-color: #dbdbdb;" class="CClumaLabel" onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #e64b3d;" class="CCredLabel" onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #00a841;" class="CCgreenLabel" onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
<span contenteditable="plaintext-only" style="text-decoration-color: #2a78c8;" class="CCblueLabel" onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
</div>
<button id="CCHamburgerButton" class="circleButton" onclick="setCCFromUI(4)" title="Set Offset">&#10138</button>
<!-- ===== EXPOSURE PANEL ===== -->
<section id="tab-exposure" class="tabPanel active">
<div class="exposureRow">
<div class="expPill">
<span class="expPillLabel">ISO</span>
<div class="expPillValue">
<button class="adjBtn" onclick="adjustISO(-100)">&#9664;</button>
<span id="ISODisplay" class="expValue" contenteditable="plaintext-only"
onkeydown="ISOKeyHandler(event)" onblur="ISOBlurHandler()">—</span>
<button class="adjBtn" onclick="adjustISO(100)">&#9654;</button>
</div>
</div>
<div class="flexContainerH" id="cameraControlExposureContainer">
<div class="ccExposureSettingContainer">
<span class="exposureControlLabel">FILTER</span>
<div class="ccExposureSettingValueContainer">
<a class="expAdjArr" href="#" onclick="decreaseND()" id="NDL">&#9664</a>
<span id="ndFilterSpan" contenteditable="plaintext-only" onkeydown="NDFilterInputHandler()" onmousedown="NDFilterInputHandler()">0</span>
<a class="expAdjArr" href="#" onclick="increaseND()" id="NDR">&#9654</a>
</div>
</div>
<div class="ccExposureSettingContainer">
<span class="exposureControlLabel">GAIN</span>
<div class="ccExposureSettingValueContainer">
<a class="expAdjArr" href="#" onclick="decreaseGain()" id="GAL">&#9664</a>
<span id="gainSpan" contenteditable="plaintext-only" onkeydown="GainInputHandler()" onmousedown="GainInputHandler()">+0db</span>
<a class="expAdjArr" href="#" onclick="increaseGain()" id="GAR">&#9654</a>
</div>
</div>
<div class="ccExposureSettingContainer">
<span class="exposureControlLabel">SHUTTER</span>
<div class="ccExposureSettingValueContainer">
<a class="expAdjArr" href="#" onclick="decreaseShutter()" id="SHL">&#9664</a>
<span id="shutterSpan" contenteditable="plaintext-only" onkeydown="handleShutterInput()" onmousedown="handleShutterInput()">1/50</span>
<a class="expAdjArr" href="#" onclick="increaseShutter()" id="SHR">&#9654</a>
</div>
</div>
<div class="ccExposureSettingContainer">
<span class="exposureControlLabel" onclick="swapWBMode()" title="Click here to swap between WB and Tint" id="WBLabel">BALANCE</span>
<div class="ccExposureSettingValueContainer" id="WBValueContainer">
<a class="expAdjArr" href="#" onclick="decreaseWhiteBalance()" id="WBL">&#9664</a>
<span id="whiteBalanceSpan" contenteditable="plaintext-only" onkeydown="WBInputHandler()" onmousedown="WBInputHandler()">5600K</span>
<a class="expAdjArr" href="#" onclick="increaseWhiteBalance()" id="WBR">&#9654</a>
</div>
<div class="ccExposureSettingValueContainer dNone" id="WBTintValueContainer">
<a class="expAdjArr" href="#" onclick="decreaseWhiteBalanceTint()" id="WBTL">&#9664</a>
<span id="whiteBalanceTintSpan" contenteditable="plaintext-only" onkeydown="WBTInputHandler()" onmousedown="WBTInputHandler()">0</span>
<a class="expAdjArr" href="#" onclick="increaseWhiteBalanceTint()" id="WBLR">&#9654</a>
</div>
</div>
<div class="ccExposureSettingContainer">
<button id="AWBButton" class="circleButton" title="Make the camera do an Auto Whitebalance" onclick="cameras[ci].doAutoWhitebalance()">AW</button>
<div class="expPill">
<span class="expPillLabel">SHUTTER</span>
<div class="expPillValue">
<button class="adjBtn" onclick="decreaseShutter()">&#9664;</button>
<span id="shutterDisplay" class="expValue" contenteditable="plaintext-only"
onkeydown="handleShutterInput()" onmousedown="handleShutterInput()">—</span>
<button class="adjBtn" onclick="increaseShutter()">&#9654;</button>
</div>
</div>
<div class="flexContainerH" id="cameraControlLensContainer">
<div class="lensSliderContainer">
<span>FOCUS</span>
<input type="range" orient="vertical" max="1" min="0" step="0.001" id="focusRange" oninput="cameras[ci].PUTdata('/lens/focus', {normalised: parseFloat(this.value)})">
<button id="AFButton" class="circleButton" onclick="cameras[ci].doAutoFocus()">AF</button>
</div>
<div class="lensSliderContainer">
<span>IRIS</span>
<input type="range" orient="vertical" max="1" min="0" step="0.001" id="irisRange" oninput="cameras[ci].PUTdata('/lens/iris', {normalised: parseFloat(this.value)})">
<span id="apertureStopsLabel">X.X</span>
</div>
<div class="lensSliderContainer">
<span>ZOOM</span>
<input type="range" orient="vertical" max="1" min="0" step="0.001" id="zoomRange" oninput="cameras[ci].PUTdata('/lens/zoom', {normalised: parseFloat(this.value)})">
<span id="zoomMMLabel">XXmm</span>
<div class="expPill">
<span class="expPillLabel">ND FILTER</span>
<div class="expPillValue">
<button class="adjBtn" onclick="decreaseND()">&#9664;</button>
<span id="ndFilterDisplay" class="expValue" contenteditable="plaintext-only"
onkeydown="NDFilterInputHandler()" onmousedown="NDFilterInputHandler()">—</span>
<button class="adjBtn" onclick="increaseND()">&#9654;</button>
</div>
</div>
<div class="expPill">
<span class="expPillLabel">GAIN</span>
<div class="expPillValue">
<button class="adjBtn" onclick="decreaseGain()">&#9664;</button>
<span id="gainDisplay" class="expValue" contenteditable="plaintext-only"
onkeydown="GainInputHandler()" onmousedown="GainInputHandler()">—</span>
<button class="adjBtn" onclick="increaseGain()">&#9654;</button>
</div>
</div>
<div class="expPill" id="wbPill">
<span class="expPillLabel" id="WBLabel" onclick="swapWBMode()" title="Click to toggle WB / Tint"
style="cursor:pointer">WB &#8597;</span>
<div class="expPillValue" id="WBValueContainer">
<button class="adjBtn" onclick="decreaseWhiteBalance()">&#9664;</button>
<span id="whiteBalanceDisplay" class="expValue" contenteditable="plaintext-only"
onkeydown="WBInputHandler()" onmousedown="WBInputHandler()">—</span>
<button class="adjBtn" onclick="increaseWhiteBalance()">&#9654;</button>
</div>
<div class="expPillValue hidden" id="WBTintValueContainer">
<button class="adjBtn" onclick="decreaseWhiteBalanceTint()">&#9664;</button>
<span id="whiteBalanceTintDisplay" class="expValue" contenteditable="plaintext-only"
onkeydown="WBTInputHandler()" onmousedown="WBTInputHandler()">—</span>
<button class="adjBtn" onclick="increaseWhiteBalanceTint()">&#9654;</button>
</div>
<button class="awbBtn" onclick="cameras[ci].doAutoWhitebalance()" title="Auto White Balance">AWB</button>
</div>
</div>
<div class="flexContainerV" id="cameraControlsContainerExpanded">
<div class="flexContainerH" id="cameraControlExpandedHeadContainer">
<h2 id="cameraName">CAMERA NAME</h2>
<div id="formatDisplay">
<span id="formatCodec">CODEC</span>
<span id="formatResolution">RESOLUTION</span>
<span id="formatFPS">FPS</span>
</div>
<div id="transportControls">
<!-- These will be sticky buttons for loop, single clip, record -->
<button class="circleButton" onclick="loopHandler('Loop')" title="Loop" id="loopButton">&#8635</button>
<button class="circleButton" onclick="loopHandler('Single Clip')" title="Single Clip" id="singleClipButton">S</button>
<button class="circleButton" onclick="cameras[ci].seek(false)" title="Back">&#9204</button>
<button class="circleButton" onclick="cameras[ci].seek(true)" title="Forward">&#9205</button>
<button class="circleButton" onclick="cameras[ci].toggleRecord()" title="Record" style="color: red;">&#9210</button>
<button class="circleButton" onclick="cameras[ci].play()" title="Play">&#9654</button>
<button class="circleButton" onclick="cameras[ci].stop()" title="Stop">&#9209</button>
</div>
<h2 id="timecodeLabel">TIMECODE</h2>
<!-- AE Mode row -->
<div class="settingsRow">
<div class="settingGroup">
<label class="settingLabel">AE MODE</label>
<select id="AEmodeDropDown" class="settingSelect" onmousedown="unsavedChanges.push('AutoExposure')">
<option value="Off">Off</option>
<option value="Continuous">Continuous</option>
<option value="OneShot">One-Shot</option>
</select>
</div>
<div class="settingGroup">
<label class="settingLabel">AE TYPE</label>
<select id="AEtypeDropDown" class="settingSelect" onmousedown="unsavedChanges.push('AutoExposure')">
<option value="">None</option>
<option value="Iris">Iris Only</option>
<option value="Shutter">Shutter Only</option>
<option value="Shutter,Iris">Shutter + Iris</option>
<option value="Iris,Shutter">Iris + Shutter</option>
</select>
</div>
<button class="actionBtn" onclick="AEmodeInputHandler()">Set AE Mode</button>
<!-- WB Presets -->
<div class="settingGroup" style="margin-left: auto;">
<label class="settingLabel">WB PRESETS</label>
<div class="presetBtnRow">
<button class="presetBtn" onclick="cameras[ci].setWhiteBalancePreset(0)">Sun</button>
<button class="presetBtn" onclick="cameras[ci].setWhiteBalancePreset(1)">Tungsten</button>
<button class="presetBtn" onclick="cameras[ci].setWhiteBalancePreset(2)">Fluoro</button>
<button class="presetBtn" onclick="cameras[ci].setWhiteBalancePreset(3)">Shade</button>
<button class="presetBtn" onclick="cameras[ci].setWhiteBalancePreset(4)">Cloud</button>
</div>
</div>
</div>
<div class="flexContainerV" id="cameraControlExpandedBodyContainer">
<div class="tableControl">
<h3>Connection</h3>
<table>
<tr>
<td>Hostname</td>
<td>
<input type="text" placeholder=" Camera-Name-Here.local" id="hostnameInput" onclick="hostnameInputHandler()" onkeydown="hostnameInputHandler()" style="text-align: left;">
<button onclick="initCamera()">Connect</button>
<input type="checkbox" id="secureCheckbox">
<span id="secureCheckboxLabel">Use HTTPS</span>
<span id="connectionErrorSpan"></span>
</td>
</tr>
<tr>
<td>Send API Call</td>
<td>
<input type="radio" id="requestTypeGET" value="GET" name="manualRequestType" checked>
<label for="requestTypeGET">GET</label>
<input type="radio" id="requestTypePUT" value="PUT" name="manualRequestType">
<label for="requestTypePUT">PUT</label>
<!-- ISO direct input -->
<div class="settingsRow">
<div class="settingGroup">
<label class="settingLabel">ISO (direct)</label>
<input type="number" id="ISOInput" class="settingInput" step="100"
onkeydown="ISOInputHandler()" onmousedown="unsavedChanges.push('ISO')">
</div>
</div>
</section>
<input type="text" id="manualRequestEndpointLabel" placeholder="request endpoint">
<input type="text" id="manualRequestBodyLabel" placeholder="request body">
<button onclick="manualAPICall()">Send API Request</button>
</td>
</tr>
<tr>
<td colspan="2">
<p id="manualRequestResponseP">Send manual API requests using the above controls. See <a href="https://documents.blackmagicdesign.com/DeveloperManuals/RESTAPIforBlackmagicCameras.pdf?_v=1696143610000" target="_blank" style="color: #6e6e6e;">documentation</a> for details.</p>
</td>
</tr>
</table>
<!-- ===== COLOR SCIENCE PANEL (NEW) ===== -->
<section id="tab-colorscience" class="tabPanel">
<div class="csGrid">
<div class="csCard">
<h3 class="csCardTitle">Gamma Curve</h3>
<div class="csOptions" id="gammaOptions">
<button class="csOptionBtn" data-gamma="Blackmagic Design Film" onclick="setGamma(this)">Film</button>
<button class="csOptionBtn" data-gamma="Blackmagic Design Video" onclick="setGamma(this)">Video</button>
<button class="csOptionBtn" data-gamma="Blackmagic Design Extended Video" onclick="setGamma(this)">Extended Video</button>
<button class="csOptionBtn" data-gamma="Rec709" onclick="setGamma(this)">Rec.709</button>
<button class="csOptionBtn" data-gamma="sRGB" onclick="setGamma(this)">sRGB</button>
<button class="csOptionBtn" data-gamma="Linear" onclick="setGamma(this)">Linear</button>
<button class="csOptionBtn" data-gamma="DaVinci Intermediate" onclick="setGamma(this)">DaVinci Intermediate</button>
</div>
<div class="csCurrentRow">
<span class="csCurrentLabel">Current:</span>
<span id="currentGamma" class="csCurrentValue"></span>
</div>
</div>
<div class="csCard">
<h3 class="csCardTitle">Color Gamut</h3>
<div class="csOptions" id="gamutOptions">
<button class="csOptionBtn" data-gamut="Blackmagic Wide Gamut" onclick="setGamut(this)">BM Wide Gamut</button>
<button class="csOptionBtn" data-gamut="Blackmagic Design" onclick="setGamut(this)">BM Design</button>
<button class="csOptionBtn" data-gamut="Rec.2020" onclick="setGamut(this)">Rec.2020</button>
<button class="csOptionBtn" data-gamut="Rec.709" onclick="setGamut(this)">Rec.709</button>
<button class="csOptionBtn" data-gamut="sRGB" onclick="setGamut(this)">sRGB</button>
<button class="csOptionBtn" data-gamut="DCI-P3" onclick="setGamut(this)">DCI-P3</button>
<button class="csOptionBtn" data-gamut="Display P3" onclick="setGamut(this)">Display P3</button>
</div>
<div class="csCurrentRow">
<span class="csCurrentLabel">Current:</span>
<span id="currentGamut" class="csCurrentValue"></span>
</div>
</div>
<div class="csCard csCard--full">
<h3 class="csCardTitle">Quick Presets</h3>
<div class="csQuickPresets">
<button class="csQuickBtn" onclick="applyColorSciencePreset('braw-film')">
<span class="csQuickBtnName">BRAW Film</span>
<span class="csQuickBtnDetail">BM Wide Gamut + Film</span>
</button>
<button class="csQuickBtn" onclick="applyColorSciencePreset('braw-video')">
<span class="csQuickBtnName">BRAW Video</span>
<span class="csQuickBtnDetail">BM Design + Video</span>
</button>
<button class="csQuickBtn" onclick="applyColorSciencePreset('braw-ext')">
<span class="csQuickBtnName">BRAW Ext. Video</span>
<span class="csQuickBtnDetail">BM Design + Ext. Video</span>
</button>
<button class="csQuickBtn" onclick="applyColorSciencePreset('rec709')">
<span class="csQuickBtnName">Rec.709</span>
<span class="csQuickBtnDetail">Rec.709 + Rec709</span>
</button>
</div>
</div>
</div>
<!-- Video Format / Mode -->
<div class="sectionDivider">
<span>Video Format</span>
</div>
<div class="formatGrid">
<div class="settingGroup">
<label class="settingLabel">CODEC</label>
<select id="codecSelect" class="settingSelect">
<option value="">— current —</option>
<option value="BRAW 12:1">BRAW 12:1</option>
<option value="BRAW 8:1">BRAW 8:1</option>
<option value="BRAW 5:1">BRAW 5:1</option>
<option value="BRAW 3:1">BRAW 3:1</option>
<option value="H.265 High">H.265 High</option>
<option value="H.265 Medium">H.265 Medium</option>
<option value="H.265 Low">H.265 Low</option>
<option value="H.264 High">H.264 High</option>
<option value="H.264 Medium">H.264 Medium</option>
<option value="H.264 Low">H.264 Low</option>
<option value="ProRes 422 HQ">ProRes 422 HQ</option>
<option value="ProRes 422">ProRes 422</option>
<option value="ProRes 422 LT">ProRes 422 LT</option>
<option value="ProRes 422 Proxy">ProRes 422 Proxy</option>
<option value="ProRes 4444 XQ">ProRes 4444 XQ</option>
<option value="DNxHR">DNxHR</option>
</select>
</div>
<div class="settingGroup">
<label class="settingLabel">FRAME RATE</label>
<select id="frameRateSelect" class="settingSelect">
<option value="">— current —</option>
<option value="23.98">23.98</option>
<option value="24">24</option>
<option value="25">25</option>
<option value="29.97">29.97</option>
<option value="30">30</option>
<option value="47.95">47.95</option>
<option value="48">48</option>
<option value="50">50</option>
<option value="59.94">59.94</option>
<option value="60">60</option>
<option value="120">120</option>
</select>
</div>
<button class="actionBtn" onclick="applyVideoFormat()">Set Format</button>
<span id="formatSetStatus" class="statusNote"></span>
</div>
</section>
<!-- ===== COLOR CORRECTION PANEL ===== -->
<section id="tab-colorcorrection" class="tabPanel">
<div class="ccGrid">
<!-- Lift -->
<div class="ccRow">
<span class="ccRowLabel">Lift</span>
<div class="ccChannels">
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-luma)">L</span>
<span class="CClumaLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
</div>
<div class="tableControl">
<h3>Presets</h3>
<table>
<tr>
<td>Preset Select</td>
<td>
<select id="presetsDropDown" onmousedown="unsavedChanges.push('presets')" onchange="presetInputHandler()">
<!-- Auto-populated by updateUIPresets() -->
</select>
</td>
<td>
<button onclick="presetInputHandler()">Restore from Preset</button>
</td>
</tr>
</table>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-red)">R</span>
<span class="CCredLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
</div>
<div class="tableControl">
<h3>Exposure</h3>
<table>
<tr>
<td>ISO</td>
<td><input type="number" id="ISOInput" step="100" onkeydown="ISOInputHandler()" onmousedown="unsavedChanges.push('ISO')"></td>
</tr>
<tr>
<td>AE Mode</td>
<td>
<select id="AEmodeDropDown" onmousedown="unsavedChanges.push('AutoExposure')">
<option value="Off">Off</option>
<option value="Continuous">Continuous</option>
<option value="OneShot">One-Shot</option>
</select>
</td>
</tr>
<tr>
<td>AE Type</td>
<td>
<select id="AEtypeDropDown" onmousedown="unsavedChanges.push('AutoExposure')">
<option value="">None</option>
<option value="Iris">Iris Only</option>
<option value="Shutter">Shutter Only</option>
<option value="Shutter,Iris">Shutter + Iris</option>
<option value="Iris,Shutter">Iris + Shutter</option>
</select>
</td>
</tr>
</table>
<button style="margin: 2vh 0 0 3.5vw;" onclick="AEmodeInputHandler()">Set AE Mode</button>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-green)">G</span>
<span class="CCgreenLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
</div>
<div class="tableControl">
<h3>Contrast</h3>
<table>
<tr>
<td>Pivot</td>
<td><input type="range" max="1" min="0" step="0.001" id="CCcontrastPivotRange" oninput="cameras[ci].PUTdata('/colorCorrection/contrast', {pivot: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter((e) => {return !e.includes('CC4')});"></td>
<td>
<span id="CCcontrastPivotLabel" contenteditable="plaintext-only" onkeydown="CCInputHandler(4)" onmousedown="CCInputHandler(4)">0</span>
</td>
<td rowspan="2">
<button class="CCResetButton circleButton" onclick="resetCC(4)" title="Reset Contrast">&#10227</button>
</td>
</tr>
<tr>
<td>Adjust</td>
<td><input type="range" max="2" min="0" step="0.001" id="CCcontrastAdjustRange" oninput="cameras[ci].PUTdata('/colorCorrection/contrast', {adjust: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter((e) => {return !e.includes('CC4')});"></td>
<td>
<span id="CCcontrastAdjustLabel" contenteditable="plaintext-only" onkeydown="CCInputHandler(4)" onmousedown="CCInputHandler(4)">0</span>
</td>
</tr>
</table>
</div>
<div class="tableControl">
<h3>Color</h3>
<table>
<tr>
<td>Hue</td>
<td><input type="range" max="1" min="-1" step="0.001" id="CChueRange" oninput="cameras[ci].PUTdata('/colorCorrection/color', {hue: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter((e) => {return !e.includes('CC5')});"></td>
<td>
<span id="CCcolorHueLabel" contenteditable="plaintext-only" onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">0</span>
</td>
<td rowspan="3">
<button class="CCResetButton circleButton" onclick="resetCC(5)" title="Reset Color">&#10227</button>
</td>
</tr>
<tr>
<td>Saturation</td>
<td><input type="range" max="2" min="0" step="0.001" id="CCsaturationRange" oninput="cameras[ci].PUTdata('/colorCorrection/color', {saturation: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter((e) => {return !e.includes('CC5')});"></td>
<td>
<span id="CCcolorSatLabel" contenteditable="plaintext-only" onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">0</span>
</td>
</tr>
<tr>
<td>Luma Contribution</td>
<td><input type="range" max="1" min="0" step="0.001" id="CClumaContributionRange" oninput="cameras[ci].PUTdata('/colorCorrection/lumaContribution', {lumaContribution: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter((e) => {return !e.includes('CC5')});"></td>
<td>
<span id="CCcolorLCLabel" contenteditable="plaintext-only" onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">0</span>
</td>
</tr>
</table>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-blue)">B</span>
<span class="CCblueLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
</div>
</div>
</div>
</div>
<button class="ccApplyBtn" onclick="setCCFromUI(0)" title="Apply">&#10138;</button>
<button class="ccResetBtn" onclick="resetCC(0)" title="Reset">&#10227;</button>
</div>
<!-- Footer Div -->
<div class="flexContainerH" id="footerContainer">
<div id="footerLeft">
<span class="">(v 1.3)</span>
<span id="activeElementSpan"></span>
<!-- Gamma -->
<div class="ccRow">
<span class="ccRowLabel">Gamma</span>
<div class="ccChannels">
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-luma)">L</span>
<span class="CClumaLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
</div>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-red)">R</span>
<span class="CCredLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
</div>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-green)">G</span>
<span class="CCgreenLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
</div>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-blue)">B</span>
<span class="CCblueLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
</div>
</div>
<button class="ccApplyBtn" onclick="setCCFromUI(1)" title="Apply">&#10138;</button>
<button class="ccResetBtn" onclick="resetCC(1)" title="Reset">&#10227;</button>
</div>
<!-- Gain -->
<div class="ccRow">
<span class="ccRowLabel">Gain</span>
<div class="ccChannels">
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-luma)">L</span>
<span class="CClumaLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">1.00</span>
</div>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-red)">R</span>
<span class="CCredLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">1.00</span>
</div>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-green)">G</span>
<span class="CCgreenLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">1.00</span>
</div>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-blue)">B</span>
<span class="CCblueLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">1.00</span>
</div>
</div>
<button class="ccApplyBtn" onclick="setCCFromUI(2)" title="Apply">&#10138;</button>
<button class="ccResetBtn" onclick="resetCC(2)" title="Reset">&#10227;</button>
</div>
<!-- Offset -->
<div class="ccRow">
<span class="ccRowLabel">Offset</span>
<div class="ccChannels">
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-luma)">L</span>
<span class="CClumaLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
</div>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-red)">R</span>
<span class="CCredLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
</div>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-green)">G</span>
<span class="CCgreenLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
</div>
<div class="ccChannel">
<span class="ccChLabel" style="color: var(--ch-blue)">B</span>
<span class="CCblueLabel ccVal" contenteditable="plaintext-only"
onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
</div>
</div>
<button class="ccApplyBtn" onclick="setCCFromUI(3)" title="Apply">&#10138;</button>
<button class="ccResetBtn" onclick="resetCC(3)" title="Reset">&#10227;</button>
</div>
</div>
<div id="footerLinks">
<span><a id="documentationLink" href="#" target="_blank">YAML Documentation</a></span>
<span><a id="mediaManagerLink" href="#" target="_blank">Web Media Manager</a></span>
<span><a id="githubLink" href="https://github.com/DylanSpeiser/BM-Camera-Control-WebUI" target="_blank">GitHub</a></span>
<!-- Contrast -->
<div class="sectionDivider"><span>Contrast</span></div>
<div class="sliderGroup">
<div class="sliderRow">
<label class="sliderLabel">Pivot</label>
<input type="range" class="slider" max="1" min="0" step="0.001" id="CCcontrastPivotRange"
oninput="cameras[ci].PUTdata('/colorCorrection/contrast', {pivot: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter(e => !e.includes('CC4'));">
<span id="CCcontrastPivotLabel" class="sliderVal" contenteditable="plaintext-only"
onkeydown="CCInputHandler(4)" onmousedown="CCInputHandler(4)">0.50</span>
</div>
<div class="sliderRow">
<label class="sliderLabel">Adjust</label>
<input type="range" class="slider" max="2" min="0" step="0.001" id="CCcontrastAdjustRange"
oninput="cameras[ci].PUTdata('/colorCorrection/contrast', {adjust: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter(e => !e.includes('CC4'));">
<span id="CCcontrastAdjustLabel" class="sliderVal" contenteditable="plaintext-only"
onkeydown="CCInputHandler(4)" onmousedown="CCInputHandler(4)">50%</span>
</div>
<button class="ccResetBtn" onclick="resetCC(4)" title="Reset Contrast">&#10227;</button>
</div>
</div>
</body>
</html>
<!-- Hue / Sat / LC -->
<div class="sectionDivider"><span>Color</span></div>
<div class="sliderGroup">
<div class="sliderRow">
<label class="sliderLabel">Hue</label>
<input type="range" class="slider" max="1" min="-1" step="0.001" id="CChueRange"
oninput="cameras[ci].PUTdata('/colorCorrection/color', {hue: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter(e => !e.includes('CC5'));">
<span id="CCcolorHueLabel" class="sliderVal" contenteditable="plaintext-only"
onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">180°</span>
</div>
<div class="sliderRow">
<label class="sliderLabel">Saturation</label>
<input type="range" class="slider" max="2" min="0" step="0.001" id="CCsaturationRange"
oninput="cameras[ci].PUTdata('/colorCorrection/color', {saturation: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter(e => !e.includes('CC5'));">
<span id="CCcolorSatLabel" class="sliderVal" contenteditable="plaintext-only"
onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">50%</span>
</div>
<div class="sliderRow">
<label class="sliderLabel">Luma Contrib.</label>
<input type="range" class="slider" max="1" min="0" step="0.001" id="CClumaContributionRange"
oninput="cameras[ci].PUTdata('/colorCorrection/lumaContribution', {lumaContribution: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter(e => !e.includes('CC5'));">
<span id="CCcolorLCLabel" class="sliderVal" contenteditable="plaintext-only"
onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">100%</span>
</div>
<button class="ccResetBtn" onclick="resetCC(5)" title="Reset Color">&#10227;</button>
</div>
</section>
<!-- ===== LENS PANEL ===== -->
<section id="tab-lens" class="tabPanel">
<div class="lensGrid">
<div class="lensCard">
<span class="lensCardLabel">FOCUS</span>
<input type="range" class="vertSlider" orient="vertical" max="1" min="0" step="0.001"
id="focusRange"
oninput="cameras[ci].PUTdata('/lens/focus', {normalised: parseFloat(this.value)})">
<button class="lensActionBtn" onclick="cameras[ci].doAutoFocus()">AF</button>
</div>
<div class="lensCard">
<span class="lensCardLabel">IRIS</span>
<input type="range" class="vertSlider" orient="vertical" max="1" min="0" step="0.001"
id="irisRange"
oninput="cameras[ci].PUTdata('/lens/iris', {normalised: parseFloat(this.value)})">
<span id="apertureStopsLabel" class="lensValueLabel">f/—</span>
</div>
<div class="lensCard">
<span class="lensCardLabel">ZOOM</span>
<input type="range" class="vertSlider" orient="vertical" max="1" min="0" step="0.001"
id="zoomRange"
oninput="cameras[ci].PUTdata('/lens/zoom', {normalised: parseFloat(this.value)})">
<span id="zoomMMLabel" class="lensValueLabel">—mm</span>
</div>
</div>
</section>
<!-- ===== SYSTEM PANEL ===== -->
<section id="tab-system" class="tabPanel">
<div class="sysGrid">
<div class="sysCard">
<h3 class="sysCardTitle">Connection</h3>
<div class="sysRow">
<label class="settingLabel">Hostname / IP</label>
<input type="text" id="hostnameInput" class="settingInput hostnameInput"
placeholder="Camera-Name.local"
onclick="hostnameInputHandler()" onkeydown="hostnameInputHandler()">
</div>
<div class="sysRow sysRow--inline">
<input type="checkbox" id="secureCheckbox">
<label for="secureCheckbox" class="settingLabel" style="cursor:pointer">Use HTTPS</label>
<button class="actionBtn" onclick="initCamera()">Connect</button>
<span id="connectionErrorSpan" class="statusNote"></span>
</div>
</div>
<div class="sysCard">
<h3 class="sysCardTitle">Presets</h3>
<div class="sysRow sysRow--inline">
<select id="presetsDropDown" class="settingSelect" style="flex:1"
onmousedown="unsavedChanges.push('presets')" onchange="presetInputHandler()">
</select>
<button class="actionBtn" onclick="presetInputHandler()">Restore Preset</button>
</div>
</div>
<div class="sysCard sysCard--full">
<h3 class="sysCardTitle">Manual API</h3>
<div class="sysRow sysRow--inline">
<label class="settingLabel">
<input type="radio" id="requestTypeGET" value="GET" name="manualRequestType" checked>
GET
</label>
<label class="settingLabel">
<input type="radio" id="requestTypePUT" value="PUT" name="manualRequestType">
PUT
</label>
<input type="text" id="manualRequestEndpointLabel" class="settingInput" placeholder="/endpoint">
<input type="text" id="manualRequestBodyLabel" class="settingInput" placeholder="JSON body">
<button class="actionBtn" onclick="manualAPICall()">Send</button>
</div>
<p id="manualRequestResponseP" class="apiResponse">
See <a href="https://documents.blackmagicdesign.com/DeveloperManuals/RESTAPIforBlackmagicCameras.pdf?_v=1696143610000"
target="_blank">BM REST API docs</a> for available endpoints.
</p>
</div>
<div class="sysCard">
<h3 class="sysCardTitle">Links</h3>
<div class="linkRow">
<a id="documentationLink" href="#" target="_blank" class="sysLink">YAML Docs</a>
<a id="mediaManagerLink" href="#" target="_blank" class="sysLink">Web Media Manager</a>
<a href="https://github.com/DylanSpeiser/BM-Camera-Control-WebUI" target="_blank" class="sysLink">GitHub</a>
</div>
</div>
</div>
</section>
</main>
<!-- Status Bar -->
<footer id="statusBar">
<span id="activeElementSpan"></span>
<span class="version">v1.5.0</span>
</footer>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
<style type="text/css">
.st0{fill:#1025A1;}
</style>
<g>
<g>
<g transform="translate(192.760202, 192.018739)">
</g>
</g>
<g>
<g>
<g>
<path d="M280,607.4c-76.3-6.3-92.6-71.7-83-105.7c0,0,36.2,14.7,49.9,49.5c0,0-1.5-31.2-13.8-40.1c0,0,91.5,22.6,87.6,81.3
c0,0,28.8-40.5-19.8-85.6c-48.6-45-40.5-53.1-40.5-53.1s8.2-4.3,12.9-11.6l1.8,4.9c0,0,2.8-11.5,2.1-16.6c0,0,3.4,6,4.8,8.1
c0,0-2.2-15.2-2.2-18.9c0-3.7,9.4-14.4,20.8-13c11.4,1.4,20.2-5.9,20.2-5.9l-1.7,6.7c0,0,6.8-6.8,11.7-7.3
c4.9-0.4,14.3,2.2,11,4.6c-3,2.2,0.3,6.9,0.7,7.4c-0.2-0.5-2-4.4,2.5-6.8c4.8-2.6,11-4.1,8.2-9.9c0,0-1.8-5.5-7.1-8.5
c0,0,1.7,3.1-0.7,3.1c-2.4,0-8.5-8.9-15.2-10.4l0.8,2.6c0,0-2.1-2.4-23.1-10.1c-20.1-7.3-21.9-12.3-22-12.8
c0,0.6,0.3,9.6,19.6,19c-0.9-0.2-15.1-4.4-23.5-12.1c0,0,4.6,7.3,14.7,12.7l-14.7-1.1l9.4,4c0,0-16.1,0.7-31.8-4.5
c0,0,4.6,3,7.8,4.7c0,0-20.4,1.5-28.5-0.2c1.8,0.5,16.5,4.4,21,5c0,0-18.4,1.3-28.7,6.9c0,0,2.3,19.5,14.4,41
c-1.3-1.5-24.2-29.3-16.6-69.8c0,0-29-5-46.6,29.6s-19,77.1,10.7,99c-0.9-0.5-15-9.1-22.2-27.9C151.5,540.5,187,578,200,588.9
c-58.5-45.5-37.5-116.9-32.2-132.2c-2.2-10.1-2.2-22.4,1.7-37.2c0,0-54.4,69.2-3.2,153.1c51.2,83.8,196.1,73.8,233.8-23.4
C398.9,551.1,355.1,613.6,280,607.4z M212.6,385.6c-0.3,6.6,0.2,13.2,1.3,19.6c0.5,3.2,1,6.5,1.9,9.6l1.1,4.8l1.5,4.7
c3.9,12.5,9.4,24.4,16.3,35.5c3.6,5.4,7,11,11.3,16c2.1,2.5,3.9,5.3,6.2,7.6c2.2,2.4,4.4,4.8,6.6,7.2c2.1,2.5,4.6,4.7,7,6.9
l7.2,6.7c4.9,4.3,10.2,8.3,15.2,12.6c-5.5-3.7-10.9-7.5-16.2-11.4l-7.6-6.4c-2.5-2.2-5.1-4.2-7.4-6.7
c-4.7-4.7-9.4-9.4-13.4-14.7c-4.4-5-8-10.6-11.7-16.1c-6.8-11.5-12.5-23.6-15.9-36.5C212.4,412.2,210.7,398.7,212.6,385.6z
M203.3,401.5c0.2,4.4,0.4,8.8,0.9,13.1c0.4,4.4,0.8,8.7,1.4,13c1.2,8.6,2.6,17.3,4.5,25.8c1.8,8.5,4.2,16.9,6.9,25.2l2.1,6.2
l2.4,6.1c1.4,4.2,3.6,8,5.2,12.1c-4.8-7.4-9-15.2-12.1-23.5c-3.3-8.2-5.9-16.7-7.8-25.4c-1.9-8.6-3.2-17.4-3.8-26.2
C202.5,419.1,202.4,410.3,203.3,401.5z M192.9,468.3c-1.2-5.8-1.8-11.7-2.2-17.6c-0.3-5.9-0.3-11.9,0.1-17.7
c0.4-5.9,1.3-11.8,2.8-17.5c-0.2,5.9-0.2,11.8-0.2,17.6c0.1,5.8,0.2,11.7,0.6,17.5c0.3,5.8,0.8,11.6,1.4,17.4l2,17.5
C195.6,479.8,194,474.1,192.9,468.3z"/>
</g>
</g>
<g>
<g transform="translate(125.869582, 192.018739)">
<g>
<path d="M262.5,282c-0.3,0.5-0.6,1.2-0.9,1.8c-1.1,2-2.2,4.4-3,6.6l-14.1,39h-7.8l-8.3-22.8l-8.2,22.8h-7.8l-14.1-39
c-1-2.8-2.5-6.1-3.9-8.4h9.5c0.1,2.2,1.2,5.5,2.2,8.3l12.8,35.5l8.2-22.7l-4.6-12.6c-1-2.8-2.6-6.1-3.9-8.4h9.5
c0.1,2.2,1.1,5.5,2.1,8.3l12.9,35.5l12.8-35.5c1-2.8-0.3-5.7-1.8-8.3H262.5z"/>
</g>
</g>
</g>
<g>
<g transform="translate(159.727017, 192.018739)">
<g>
<path d="M268.2,290.5c0-3.2,0.3-6,1.1-8.4h-9.5c0.9,2.4,1.1,5.2,1.1,8.4c0,5.2,0,25.4,0,30.6c0,3.2-0.3,6-1.1,8.4h31.4l0.4-7.2
H291c-0.5,1.3-2.7,4.7-8.3,4.7h-14.5V290.5z"/>
</g>
</g>
</g>
<g>
<g transform="translate(174.231269, 192.018739)">
<g>
<path d="M306.6,282h-18.8c0.9,2.4,1.1,5.2,1.1,8.4v30.6c0,3.2-0.2,6-1.1,8.4h18.8c13.1,0,24.9-8.2,24.9-23.7
C331.5,290.2,319.7,282,306.6,282z M306.6,327h-10.4v-42.5h10.4c9,0,17.4,8.1,17.4,21.2C324,318.8,315.6,327,306.6,327z"/>
</g>
</g>
</g>
<g>
<g transform="translate(202.027605, 192.018739)">
<g>
<path d="M360.3,282h-18.8c0.9,2.4,1.1,5.2,1.1,8.4v30.6c0,3.2-0.3,6-1.1,8.4h18.8c13.1,0,24.8-8.2,24.8-23.7
C385.2,290.2,373.4,282,360.3,282z M360.3,327H350v-42.5h10.4c9,0,17.4,8.1,17.4,21.2C377.7,318.8,369.3,327,360.3,327z"/>
</g>
</g>
</g>
<g>
<g transform="translate(220.56186, 192.018739)">
<g>
<path d="M407,321.1l-8.2-15.2c6.4-3.1,12.6-6.2,12.6-13.7c0-5.5-4.9-10.1-15.4-10.1h-18.7c0.9,2.4,1.1,5.2,1.1,8.4v30.6
c0,3.2-0.3,6-1.1,8.4h9.5c-0.9-2.4-1.1-5.2-1.1-8.4v-1.3c0-5,2.9-8.1,6.6-10.5l7.8,14.6c1,1.8,1.7,3.6,2.2,5.6h10
C410.8,327.4,408.5,323.8,407,321.1z M385.8,312.1v-27.9h3.8c7.6,0,14.4,1.7,14.6,7.5C404.6,301.5,391.1,303.2,385.8,312.1z"/>
</g>
</g>
</g>
<g>
<g transform="translate(236.122104, 192.018739)">
<g>
<path d="M447.4,321.1L433.3,282h-7.8l-14.1,39.1c-1,2.8-2.5,6-3.9,8.3h8.3c-2-3.4-2.6-6-1.8-8.3c1.1-3.1,2.3-5.6,5.2-7.3
c4.4-2.6,11.2-2.5,15.6,0.2c2.6,1.6,3.8,4,4.9,7.3c1,2.7,2.1,6,2.1,8.2h9.6C450,327.1,448.4,323.9,447.4,321.1z M417.2,312.1
l9.6-26.4l9.6,26.4C430.8,308.5,422.7,308.5,417.2,312.1z"/>
</g>
</g>
</g>
<g>
<g transform="translate(254.721007, 192.018739)">
<g>
<path d="M468,281.2c-13.7,0-24.7,10.9-24.7,24.5c0,13.7,10.9,24.7,24.7,24.7c5,0,9.7-1.5,13.8-4.2c3.1-2.2,6.3-1.3,7.1,2.6h0.6
v-13.8c0-3.3,0.2-6.2,1.1-8.5h-9.9c1,2.3,1.3,5.2,1.3,8.5v6.2c-4,5.2-11.4,7.3-17.6,5.2c-7.5-2.7-13.5-10.5-13.5-20.6
c0-13.2,9.7-22.1,19.8-21.6c9.3,0.5,16.2,7.5,18.4,15.7h0.8v-11c-1.3,0.1-3.4-0.9-4.8-2C479.4,282.9,474.9,281.2,468,281.2z"/>
</g>
</g>
</g>
<g>
<g transform="translate(274.462109, 192.018739)">
<g>
<path d="M506.1,281.2c-13.6,0-24.6,11-24.6,24.6c0,13.6,11,24.6,24.6,24.6c13.6,0,24.6-11,24.6-24.6
C530.7,292.2,519.7,281.2,506.1,281.2z M516.8,325c-8.3,4.6-19.7-0.4-25.6-11.1c-5.9-10.6-3.9-22.9,4.3-27.5
c8.2-4.5,19.7,0.5,25.5,11.1C526.9,308.2,525,320.6,516.8,325z"/>
</g>
</g>
</g>
<g>
<g transform="translate(294.892833, 192.018739)">
<g>
<path d="M552.3,282c3.9,2.2,4.7,7.3,4.7,11.3v25L530.1,282c-3,0-6.1,0-9.1,0l1.1,1.5v37.5c0,3.2-0.3,6-1.1,8.4h8.3
c-3.8-2.2-4.7-7.3-4.7-11.3v-31.2l31.6,42.5h3.3v-39c0-3.2,0.3-6,1.1-8.4H552.3z"/>
</g>
</g>
</g>
<path class="st0" d="M419,447.1c0,0,4.5-13.4-12.3-25.4c-16.2-11.6-7.1-24-6.5-24.8c-0.7,0.4-8.4,5.2-3.9,22.3
c4.8,17.9,1.7,28.5,1.7,28.5s-8.4,0-17-11.5c0,0-0.6,14.3,11.5,17.6c12,3.4,15.1,15.1,7.8,15.9c-7.3,0.8-9.9-15.5-20.9-15.7
c0,0,11.9,5.7,8,12.6c-4,6.9-9.2-5-9.2-5s0.1,7.9,6.3,12.5c0.9,0.7,2,1.3,3.1,1.8c1.2,0.5,2.6,0.9,4.2,1.2
c3.3,0.5,6.5,4.3,8.7,10.2c3,8.2,4.1,20.4,1.2,34.2c-3.4,16.5-12.5,35.1-30.9,51.4c1-0.6,26.5-17,37.7-51.4
c0.1-0.2,0.2-0.4,0.2-0.6c2.6-8,4.3-17,4.9-26.9c2.8-54.1,29.3-51.3,29.3-51.3S429.6,437.5,419,447.1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

1132
style.css

File diff suppressed because it is too large Load diff

1031
web-ui.js

File diff suppressed because it is too large Load diff