Files
Strix/webui/web/index.html
T
eduard256 7fd1d78ffa Add dual-stream support for Frigate with optional sub-stream selection
Features:
- Optional sub-stream selection from already discovered streams
- No additional scanning required - reuse existing results
- UI: "Add Sub Stream" button to select secondary stream
- UI: "Remove Sub Stream" button to clear selection
- Smart stream routing in Frigate configs
- Go2RTC: generates _main and _sub stream names
- Frigate: detect on sub (CPU efficient), record on main (quality)
- Frigate: auto-detection of stream resolution
- Object detection: person, car, cat, dog
- Motion-based recording by default
- Live view streams configuration
- Support for any resolution: HD, 4K, 8K+
- Comprehensive documentation with examples
2025-11-06 22:53:50 +03:00

330 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="theme-color" content="#0a0a0f">
<title>Strix - Camera Stream Discovery</title>
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<div id="app">
<!-- Screen 1: Initial Address Input -->
<div id="screen-address" class="screen active">
<div class="container">
<div class="hero">
<svg class="logo" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="24" cy="20" r="6" stroke="currentColor" stroke-width="2"/>
<path d="M12 20c0-6.627 5.373-12 12-12s12 5.373 12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<circle cx="18" cy="18" r="2" fill="currentColor"/>
<circle cx="30" cy="18" r="2" fill="currentColor"/>
<path d="M20 28l4-2 4 2v8l-4-2-4 2z" fill="currentColor"/>
</svg>
<h1 class="title">STRIX</h1>
<p class="subtitle">Camera Stream Discovery</p>
</div>
<div class="form-group">
<label for="network-address" class="label">Network Address</label>
<input
type="text"
id="network-address"
class="input input-large"
placeholder="192.168.1.100"
autocomplete="off"
spellcheck="false"
>
<p class="hint">IP, hostname or full stream URL</p>
</div>
<button id="btn-check-address" class="btn btn-primary btn-large">
Check Address
</button>
<div class="examples">
<p class="examples-title">Examples</p>
<ul class="examples-list">
<li>192.168.1.100</li>
<li>camera.local</li>
<li>rtsp://user:pass@192.168.1.100/stream</li>
</ul>
</div>
</div>
</div>
<!-- Screen 2: Configuration Form -->
<div id="screen-config" class="screen">
<div class="container">
<button id="btn-back-to-address" class="btn-back">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M12 4L6 10l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
Back
</button>
<h2 class="screen-title">Camera Configuration</h2>
<div class="form-group">
<label for="address-validated" class="label">Network Address</label>
<div class="input-validated">
<input
type="text"
id="address-validated"
class="input"
readonly
>
<svg class="icon-check" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M4 10l4 4 8-8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</div>
</div>
<div class="form-group">
<label for="camera-model" class="label">Camera Model <span class="optional">(optional)</span></label>
<div class="autocomplete-wrapper">
<input
type="text"
id="camera-model"
class="input"
placeholder="Start typing..."
autocomplete="off"
spellcheck="false"
>
<div id="autocomplete-dropdown" class="autocomplete-dropdown hidden"></div>
</div>
<p id="model-disabled-hint" class="hint hidden">Detected from URL, continue below</p>
</div>
<div class="form-group">
<label for="username" class="label">Username</label>
<input
type="text"
id="username"
class="input"
placeholder="admin"
autocomplete="off"
>
</div>
<div class="form-group">
<label for="password" class="label">Password</label>
<div class="input-password-wrapper">
<input
type="password"
id="password"
class="input"
placeholder="••••••••"
autocomplete="off"
>
<button type="button" class="btn-toggle-password" aria-label="Toggle password visibility">
<svg class="icon-eye" width="20" height="20" viewBox="0 0 20 20" fill="none">
<circle cx="10" cy="10" r="3" stroke="currentColor" stroke-width="1.5"/>
<path d="M2 10c1.5-4 4-6.5 8-6.5s6.5 2.5 8 6.5c-1.5 4-4 6.5-8 6.5S3.5 14 2 10z" stroke="currentColor" stroke-width="1.5"/>
<circle cx="10" cy="10" r="1.5" fill="currentColor"/>
</svg>
</button>
</div>
</div>
<details class="advanced-section">
<summary class="advanced-toggle">Advanced</summary>
<div class="advanced-content">
<div class="form-group">
<label for="channel" class="label">Channel</label>
<input
type="number"
id="channel"
class="input"
value="0"
min="0"
max="255"
>
</div>
<div class="form-group">
<label class="label">Resolution <span class="optional">(optional)</span></label>
<div class="input-row">
<input
type="number"
id="width"
class="input"
placeholder="Width"
>
<span class="input-separator">×</span>
<input
type="number"
id="height"
class="input"
placeholder="Height"
>
</div>
</div>
<div class="form-group">
<label for="max-streams" class="label">Max Streams</label>
<input
type="number"
id="max-streams"
class="input"
value="10"
min="1"
max="50"
>
</div>
</div>
</details>
<button id="btn-discover" class="btn btn-primary btn-large">
Discover Streams
</button>
</div>
</div>
<!-- Screen 3: Stream Discovery -->
<div id="screen-discovery" class="screen">
<div class="container">
<button id="btn-back-to-config" class="btn-back">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M12 4L6 10l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
Back to Configuration
</button>
<h2 class="screen-title">Discovering Streams</h2>
<div class="progress-container">
<div class="progress-bar">
<div id="progress-fill" class="progress-fill"></div>
</div>
<p id="progress-text" class="progress-text">Starting scan...</p>
</div>
<div class="stats">
<div class="stat">
<span class="stat-value" id="stat-tested">0</span>
<span class="stat-label">Tested</span>
</div>
<div class="stat">
<span class="stat-value stat-primary" id="stat-found">0</span>
<span class="stat-label">Found</span>
</div>
<div class="stat">
<span class="stat-value" id="stat-remaining">0</span>
<span class="stat-label">Remaining</span>
</div>
</div>
<div id="streams-section" class="streams-section hidden">
<h3 class="section-title">Found Connections</h3>
<div class="carousel-wrapper">
<button id="carousel-prev" class="carousel-arrow carousel-arrow-left" aria-label="Previous stream">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M15 18l-6-6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</button>
<div class="carousel">
<div id="carousel-track" class="carousel-track"></div>
</div>
<button id="carousel-next" class="carousel-arrow carousel-arrow-right" aria-label="Next stream">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</button>
</div>
<div class="carousel-info">
<p id="carousel-counter" class="carousel-counter">Stream 1 of 1</p>
<div id="carousel-dots" class="carousel-dots"></div>
</div>
</div>
</div>
</div>
<!-- Screen 4: Configuration Output -->
<div id="screen-output" class="screen">
<div class="container">
<button id="btn-back-to-streams" class="btn-back">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M12 4L6 10l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
Back to Streams
</button>
<h2 class="screen-title">Stream Configuration</h2>
<div class="stream-selection-container">
<div class="selected-stream-info">
<p class="stream-label">Main Stream</p>
<p id="selected-main-type" class="selected-type"></p>
<p id="selected-main-url" class="selected-url"></p>
</div>
<div id="sub-stream-info" class="selected-stream-info sub-stream hidden">
<p class="stream-label">Sub Stream</p>
<p id="selected-sub-type" class="selected-type"></p>
<p id="selected-sub-url" class="selected-url"></p>
<button id="btn-remove-sub" class="btn-remove-sub">Remove Sub Stream</button>
</div>
</div>
<div class="tabs">
<div class="tabs-scroll">
<button class="tab active" data-tab="url">URL</button>
<button class="tab" data-tab="go2rtc">Go2RTC</button>
<button class="tab" data-tab="frigate">Frigate</button>
</div>
</div>
<div class="tab-content">
<div class="tab-pane active" data-pane="url">
<pre id="config-url" class="config-code"></pre>
</div>
<div class="tab-pane" data-pane="go2rtc">
<pre id="config-go2rtc" class="config-code"></pre>
</div>
<div class="tab-pane" data-pane="frigate">
<pre id="config-frigate" class="config-code"></pre>
</div>
</div>
<div class="actions">
<button id="btn-copy-config" class="btn btn-secondary">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<rect x="6" y="6" width="10" height="10" rx="1" stroke="currentColor" stroke-width="1.5"/>
<path d="M4 4h10v2H5v9H4V4z" fill="currentColor"/>
</svg>
Copy
</button>
<button id="btn-download-config" class="btn btn-secondary">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M10 3v10m0 0l-4-4m4 4l4-4M4 17h12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
Download
</button>
</div>
<div class="secondary-actions">
<button id="btn-add-sub-stream" class="btn btn-primary">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M10 4v12M4 10h12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
Add Sub Stream
</button>
<button id="btn-new-search" class="btn btn-outline">
Add Another Camera
</button>
</div>
</div>
</div>
</div>
<!-- Toast Notification -->
<div id="toast" class="toast hidden"></div>
<script type="module" src="/js/main.js"></script>
</body>
</html>