Add Frigate config merging and camera database updates
- Refactor Frigate generator to support adding cameras to existing configs - Add text-based YAML parsing to preserve formatting and comments - Implement duplicate camera/stream name detection and auto-numbering - Add support for inserting cameras into existing go2rtc and cameras sections - Update UI: add textarea for existing config input and generate button - Preserve user's existing configuration when adding new cameras - Add example config template for new users - Update ConfigPanel to initialize Frigate tab instead of auto-generating - Add FrigateGenerator import to main.js - Add custom styles for Frigate config input and output sections - Support both empty config (create from scratch) and existing config (merge) modes Camera database updates: - Add OpenIPC firmware camera support (257 models) - Add Yi-Hack firmware variants (v4, v5, Allwinner, MStar) - Add Fang-Hacks firmware support - Add OpenMiko firmware support - Update Sonoff camera models - Update Thingino firmware camera models
This commit is contained in:
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"brand": "fang-hacks",
|
||||||
|
"brand_id": "fang-hacks",
|
||||||
|
"last_updated": "2025-11-11",
|
||||||
|
"source": "github.com/samtap/fang-hacks",
|
||||||
|
"website": "https://github.com/samtap/fang-hacks",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"XIAOFANG ARM PROCESSOR",
|
||||||
|
"XiaoFang ARM",
|
||||||
|
"Classic XiaoFang",
|
||||||
|
"ARM926EJ-S"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/unicast",
|
||||||
|
"notes": "Classic XiaoFang with ARM processor (not Ingenic)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"XIAOMI XIAOFANG",
|
||||||
|
"Xiaofang",
|
||||||
|
"XiaoFang"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/unicast",
|
||||||
|
"notes": "Xiaomi Xiaofang camera with fang-hacks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/unicast",
|
||||||
|
"notes": "Generic fang-hacks installation"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
{
|
||||||
|
"brand": "OpenIPC",
|
||||||
|
"brand_id": "openipc",
|
||||||
|
"last_updated": "2025-11-11",
|
||||||
|
"source": "openipc.org",
|
||||||
|
"website": "https://openipc.org",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"MAJESTIC STREAMER",
|
||||||
|
"Generic",
|
||||||
|
"Majestic",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=0",
|
||||||
|
"notes": "Main stream (video0) - Majestic streamer default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"MAJESTIC STREAMER",
|
||||||
|
"Generic",
|
||||||
|
"Majestic",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=1",
|
||||||
|
"notes": "Sub stream (video1) - Majestic streamer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"HISILICON",
|
||||||
|
"Hi3516EV200",
|
||||||
|
"Hi3516EV300",
|
||||||
|
"Hi3516CV500",
|
||||||
|
"Hi3516DV300",
|
||||||
|
"Hi3518EV200",
|
||||||
|
"Hi3518EV300"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=0",
|
||||||
|
"notes": "HiSilicon SoC main stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"HISILICON",
|
||||||
|
"Hi3516EV200",
|
||||||
|
"Hi3516EV300",
|
||||||
|
"Hi3516CV500",
|
||||||
|
"Hi3516DV300",
|
||||||
|
"Hi3518EV200",
|
||||||
|
"Hi3518EV300"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=1",
|
||||||
|
"notes": "HiSilicon SoC sub stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"GOKE",
|
||||||
|
"GK7205V200",
|
||||||
|
"GK7205V300",
|
||||||
|
"GK7605V100"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=0",
|
||||||
|
"notes": "Goke SoC main stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"GOKE",
|
||||||
|
"GK7205V200",
|
||||||
|
"GK7205V300",
|
||||||
|
"GK7605V100"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=1",
|
||||||
|
"notes": "Goke SoC sub stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"INGENIC",
|
||||||
|
"T31",
|
||||||
|
"T30",
|
||||||
|
"T20",
|
||||||
|
"T10"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=0",
|
||||||
|
"notes": "Ingenic SoC main stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"INGENIC",
|
||||||
|
"T31",
|
||||||
|
"T30",
|
||||||
|
"T20",
|
||||||
|
"T10"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=1",
|
||||||
|
"notes": "Ingenic SoC sub stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"SIGMASTAR",
|
||||||
|
"SSC325",
|
||||||
|
"SSC335",
|
||||||
|
"SSC337",
|
||||||
|
"SSC338Q"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=0",
|
||||||
|
"notes": "SigmaStar SoC main stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"SIGMASTAR",
|
||||||
|
"SSC325",
|
||||||
|
"SSC335",
|
||||||
|
"SSC337",
|
||||||
|
"SSC338Q"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=1",
|
||||||
|
"notes": "SigmaStar SoC sub stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"NOVATEK",
|
||||||
|
"NT98562",
|
||||||
|
"NT98566"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=0",
|
||||||
|
"notes": "Novatek SoC main stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"XIONGMAI",
|
||||||
|
"XM530",
|
||||||
|
"XM550"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=0",
|
||||||
|
"notes": "XiongMai SoC main stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"AMBARELLA",
|
||||||
|
"S2L",
|
||||||
|
"S3L"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/stream=0",
|
||||||
|
"notes": "Ambarella SoC main stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Generic",
|
||||||
|
"Majestic",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "JPEG",
|
||||||
|
"protocol": "http",
|
||||||
|
"port": 80,
|
||||||
|
"url": "/image.jpg",
|
||||||
|
"notes": "JPEG snapshot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Generic",
|
||||||
|
"Majestic",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "MJPEG",
|
||||||
|
"protocol": "http",
|
||||||
|
"port": 80,
|
||||||
|
"url": "/mjpeg.html",
|
||||||
|
"notes": "MJPEG stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Generic",
|
||||||
|
"Majestic",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "http",
|
||||||
|
"port": 80,
|
||||||
|
"url": "/video.mp4",
|
||||||
|
"notes": "Fragmented MP4 video"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Generic",
|
||||||
|
"Majestic",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "http",
|
||||||
|
"port": 80,
|
||||||
|
"url": "/audio.opus",
|
||||||
|
"notes": "Opus audio stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Generic",
|
||||||
|
"Majestic",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "http",
|
||||||
|
"port": 80,
|
||||||
|
"url": "/audio.mp3",
|
||||||
|
"notes": "MP3 audio stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Generic",
|
||||||
|
"Majestic",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "http",
|
||||||
|
"port": 80,
|
||||||
|
"url": "/audio.m4a",
|
||||||
|
"notes": "AAC audio stream"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"brand": "OpenMiko",
|
||||||
|
"brand_id": "openmiko",
|
||||||
|
"last_updated": "2025-11-11",
|
||||||
|
"source": "github.com/openmiko/openmiko",
|
||||||
|
"website": "https://github.com/openmiko/openmiko",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"WYZE CAM V2",
|
||||||
|
"WyzeCam V2",
|
||||||
|
"Wyze V2",
|
||||||
|
"WYZEC1-JZ"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 8554,
|
||||||
|
"url": "/video3_unicast",
|
||||||
|
"notes": "WyzeCam V2 with OpenMiko firmware - NON-STANDARD PORT 8554"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"XIAOMI XIAOFANG 1S",
|
||||||
|
"Xiaofang 1S",
|
||||||
|
"XiaoFang 1S"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 8554,
|
||||||
|
"url": "/video3_unicast",
|
||||||
|
"notes": "Xiaomi Xiaofang 1S with OpenMiko - NON-STANDARD PORT 8554"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"ISMARTALARM SPOT+",
|
||||||
|
"iSmartAlarm Spot+",
|
||||||
|
"Spot+"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 8554,
|
||||||
|
"url": "/video3_unicast",
|
||||||
|
"notes": "iSmartAlarm Spot+ with OpenMiko - NON-STANDARD PORT 8554"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"INGENIC T20",
|
||||||
|
"Generic T20",
|
||||||
|
"T20 based",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 8554,
|
||||||
|
"url": "/video3_unicast",
|
||||||
|
"notes": "Generic Ingenic T20 based cameras - NON-STANDARD PORT 8554"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+40
-2
@@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"brand": "Sonoff",
|
"brand": "Sonoff",
|
||||||
"brand_id": "sonoff",
|
"brand_id": "sonoff",
|
||||||
"last_updated": "2025-10-17",
|
"last_updated": "2025-11-11",
|
||||||
"source": "ispyconnect.com",
|
"source": "ispyconnect.com, github.com/roleoroleo/sonoff-hack",
|
||||||
|
"website": "https://github.com/roleoroleo/sonoff-hack",
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
{
|
||||||
"models": [
|
"models": [
|
||||||
@@ -73,6 +74,43 @@
|
|||||||
"protocol": "rtsp",
|
"protocol": "rtsp",
|
||||||
"port": 0,
|
"port": 0,
|
||||||
"url": "live/ch00_0"
|
"url": "live/ch00_0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"SONOFF-HACK GOKE GK7205V200",
|
||||||
|
"GK-200MP2-B with sonoff-hack",
|
||||||
|
"Goke GK7205V200",
|
||||||
|
"sonoff-hack"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/av_stream/ch0",
|
||||||
|
"notes": "Sonoff-hack custom firmware - Main stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"SONOFF-HACK",
|
||||||
|
"GK-200MP2-B with sonoff-hack",
|
||||||
|
"sonoff-hack"
|
||||||
|
],
|
||||||
|
"type": "JPEG",
|
||||||
|
"protocol": "http",
|
||||||
|
"port": 8080,
|
||||||
|
"url": "/snapshot.jpg",
|
||||||
|
"notes": "Sonoff-hack custom firmware - JPEG snapshot (port 8080)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"SONOFF-HACK GOKE GK7205V300",
|
||||||
|
"GK7205V300",
|
||||||
|
"Goke chipset"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/av_stream/ch0",
|
||||||
|
"notes": "Sonoff-hack for Goke GK7205V300 chipset"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+146
-3
@@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"brand": "Thingino",
|
"brand": "Thingino",
|
||||||
"brand_id": "thingino",
|
"brand_id": "thingino",
|
||||||
"last_updated": "2025-10-17",
|
"last_updated": "2025-11-11",
|
||||||
"source": "ispyconnect.com",
|
"source": "ispyconnect.com, thingino.com",
|
||||||
|
"website": "https://github.com/themactep/thingino-firmware",
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
{
|
||||||
"models": [
|
"models": [
|
||||||
@@ -13,6 +14,148 @@
|
|||||||
"protocol": "rtsp",
|
"protocol": "rtsp",
|
||||||
"port": 554,
|
"port": 554,
|
||||||
"url": "/ch0"
|
"url": "/ch0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"INGENIC T31",
|
||||||
|
"T31",
|
||||||
|
"T31X",
|
||||||
|
"T31ZX",
|
||||||
|
"T31A",
|
||||||
|
"Generic T31"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0",
|
||||||
|
"notes": "Main stream 1080p - Ingenic T31 series SoC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"INGENIC T31",
|
||||||
|
"T31",
|
||||||
|
"T31X",
|
||||||
|
"T31ZX",
|
||||||
|
"T31A",
|
||||||
|
"Generic T31"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch1",
|
||||||
|
"notes": "Sub stream 360p - Ingenic T31 series SoC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"INGENIC T20",
|
||||||
|
"T20",
|
||||||
|
"T20X",
|
||||||
|
"T20L",
|
||||||
|
"Generic T20"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0",
|
||||||
|
"notes": "Main stream 1080p - Ingenic T20 series SoC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"INGENIC T20",
|
||||||
|
"T20",
|
||||||
|
"T20X",
|
||||||
|
"T20L",
|
||||||
|
"Generic T20"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch1",
|
||||||
|
"notes": "Sub stream 360p - Ingenic T20 series SoC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"INGENIC T10",
|
||||||
|
"T10",
|
||||||
|
"Generic T10"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0",
|
||||||
|
"notes": "Main stream - Ingenic T10 SoC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"INGENIC T10",
|
||||||
|
"T10",
|
||||||
|
"Generic T10"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch1",
|
||||||
|
"notes": "Sub stream - Ingenic T10 SoC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"INGENIC S3L",
|
||||||
|
"S3L",
|
||||||
|
"Generic S3L"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0",
|
||||||
|
"notes": "Main stream - Ingenic S3L SoC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Generic",
|
||||||
|
"Other",
|
||||||
|
"Xiaomi Dafang",
|
||||||
|
"Xiaomi Xiaofang"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0",
|
||||||
|
"notes": "Main stream 1080p - Generic Thingino installation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Generic",
|
||||||
|
"Other",
|
||||||
|
"Xiaomi Dafang",
|
||||||
|
"Xiaomi Xiaofang"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch1",
|
||||||
|
"notes": "Sub stream 360p - Generic Thingino installation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Generic",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "JPEG",
|
||||||
|
"protocol": "http",
|
||||||
|
"port": 80,
|
||||||
|
"url": "/image.jpg",
|
||||||
|
"notes": "JPEG snapshot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Generic",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "MJPEG",
|
||||||
|
"protocol": "http",
|
||||||
|
"port": 8080,
|
||||||
|
"url": "/",
|
||||||
|
"notes": "MJPEG stream via HTTP"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"brand": "yi-hack-Allwinner-v2",
|
||||||
|
"brand_id": "yi-hack-allwinner-v2",
|
||||||
|
"last_updated": "2025-11-11",
|
||||||
|
"source": "github.com/roleoroleo/yi-hack-Allwinner-v2",
|
||||||
|
"website": "https://github.com/roleoroleo/yi-hack-Allwinner-v2",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"ALLWINNER V2 PLATFORM",
|
||||||
|
"Yi Allwinner v2",
|
||||||
|
"Different flash layout",
|
||||||
|
"Tovendor Mini Smart Home Camera"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Allwinner v2 platform (different flash layout) - High resolution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"ALLWINNER V2 PLATFORM",
|
||||||
|
"Yi Allwinner v2",
|
||||||
|
"Different flash layout",
|
||||||
|
"Tovendor Mini Smart Home Camera"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Allwinner v2 platform (different flash layout) - Low resolution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"ALLWINNER V2 PLATFORM",
|
||||||
|
"Yi Allwinner v2",
|
||||||
|
"Different flash layout",
|
||||||
|
"Tovendor Mini Smart Home Camera"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_2.h264",
|
||||||
|
"notes": "Allwinner v2 platform (different flash layout) - Audio only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI HOME CAMERA 3",
|
||||||
|
"Yi Home Camera 3"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Home Camera 3 - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI HOME CAMERA 3",
|
||||||
|
"Yi Home Camera 3"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Home Camera 3 - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Generic Allwinner v2 camera - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Generic Allwinner v2 camera - SD stream"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
{
|
||||||
|
"brand": "yi-hack-Allwinner",
|
||||||
|
"brand_id": "yi-hack-allwinner",
|
||||||
|
"last_updated": "2025-11-11",
|
||||||
|
"source": "github.com/roleoroleo/yi-hack-Allwinner",
|
||||||
|
"website": "https://github.com/roleoroleo/yi-hack-Allwinner",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"ALLWINNER PLATFORM",
|
||||||
|
"Yi 1080p Allwinner",
|
||||||
|
"Generic Allwinner"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Allwinner platform - High resolution video"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"ALLWINNER PLATFORM",
|
||||||
|
"Yi 1080p Allwinner",
|
||||||
|
"Generic Allwinner"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Allwinner platform - Low resolution video"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"ALLWINNER PLATFORM",
|
||||||
|
"Yi 1080p Allwinner",
|
||||||
|
"Generic Allwinner"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_2.h264",
|
||||||
|
"notes": "Allwinner platform - Audio only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI HOME 1080P ALLWINNER",
|
||||||
|
"Yi Home Allwinner"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Home 1080p Allwinner - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI HOME 1080P ALLWINNER",
|
||||||
|
"Yi Home Allwinner"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Home 1080p Allwinner - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI DOME 1080P ALLWINNER",
|
||||||
|
"Yi Dome Allwinner"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Dome 1080p Allwinner - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI DOME 1080P ALLWINNER",
|
||||||
|
"Yi Dome Allwinner"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Dome 1080p Allwinner - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Generic Allwinner camera - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Generic Allwinner camera - SD stream"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
{
|
||||||
|
"brand": "yi-hack-MStar",
|
||||||
|
"brand_id": "yi-hack-mstar",
|
||||||
|
"last_updated": "2025-11-11",
|
||||||
|
"source": "github.com/roleoroleo/yi-hack-MStar",
|
||||||
|
"website": "https://github.com/roleoroleo/yi-hack-MStar",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"MSTAR INFINITY CHIPSET",
|
||||||
|
"MStar",
|
||||||
|
"Yi Home MStar",
|
||||||
|
"Yi Dome MStar",
|
||||||
|
"Generic MStar"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "MStar Infinity chipset - High resolution video"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"MSTAR INFINITY CHIPSET",
|
||||||
|
"MStar",
|
||||||
|
"Yi Home MStar",
|
||||||
|
"Yi Dome MStar",
|
||||||
|
"Generic MStar"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "MStar Infinity chipset - Low resolution video"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"MSTAR INFINITY CHIPSET",
|
||||||
|
"MStar",
|
||||||
|
"Yi Home MStar",
|
||||||
|
"Yi Dome MStar",
|
||||||
|
"Generic MStar"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_2.h264",
|
||||||
|
"notes": "MStar Infinity chipset - Audio only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI HOME 1080P MSTAR",
|
||||||
|
"Yi 1080p MStar"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Home 1080p MStar - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI HOME 1080P MSTAR",
|
||||||
|
"Yi 1080p MStar"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Home 1080p MStar - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI DOME 1080P MSTAR",
|
||||||
|
"Yi Dome MStar"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Dome 1080p MStar - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI DOME 1080P MSTAR",
|
||||||
|
"Yi Dome MStar"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Dome 1080p MStar - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"AQARA CAMERA G2H",
|
||||||
|
"Aqara G2H",
|
||||||
|
"G2H"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Aqara Camera G2H with yi-hack-MStar - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"AQARA CAMERA G2H",
|
||||||
|
"Aqara G2H",
|
||||||
|
"G2H"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Aqara Camera G2H with yi-hack-MStar - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Generic MStar based camera - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Generic MStar based camera - SD stream"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
{
|
||||||
|
"brand": "yi-hack-v4",
|
||||||
|
"brand_id": "yi-hack-v4",
|
||||||
|
"last_updated": "2025-11-11",
|
||||||
|
"source": "github.com/TheCrypt0/yi-hack-v4",
|
||||||
|
"website": "https://github.com/TheCrypt0/yi-hack-v4",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI HOME 720P",
|
||||||
|
"Yi Home 720p",
|
||||||
|
"17CN",
|
||||||
|
"27US",
|
||||||
|
"47CN"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Home 720p - Hi3518e chipset - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI HOME 720P",
|
||||||
|
"Yi Home 720p",
|
||||||
|
"17CN",
|
||||||
|
"27US",
|
||||||
|
"47CN"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Home 720p - Hi3518e chipset - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI DOME 720P",
|
||||||
|
"Yi Dome 720p",
|
||||||
|
"43US",
|
||||||
|
"63US",
|
||||||
|
"Generic Dome 720p"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Dome 720p - Hi3518e chipset - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI DOME 720P",
|
||||||
|
"Yi Dome 720p",
|
||||||
|
"43US",
|
||||||
|
"63US",
|
||||||
|
"Generic Dome 720p"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Dome 720p - Hi3518e chipset - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI HOME 1080P",
|
||||||
|
"Yi Home 1080p",
|
||||||
|
"48US",
|
||||||
|
"Version 1"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Home 1080p - Hi3518e chipset - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI HOME 1080P",
|
||||||
|
"Yi Home 1080p",
|
||||||
|
"48US",
|
||||||
|
"Version 1"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Home 1080p - Hi3518e chipset - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI DOME 1080P",
|
||||||
|
"Yi Dome 1080p",
|
||||||
|
"45US",
|
||||||
|
"65US",
|
||||||
|
"Generic Dome 1080p"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Dome 1080p - Hi3518e chipset - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI DOME 1080P",
|
||||||
|
"Yi Dome 1080p",
|
||||||
|
"45US",
|
||||||
|
"65US",
|
||||||
|
"Generic Dome 1080p"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Dome 1080p - Hi3518e chipset - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI CLOUD DOME 1080P",
|
||||||
|
"Yi Cloud Dome 1080p"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Cloud Dome 1080p - Hi3518e chipset - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI CLOUD DOME 1080P",
|
||||||
|
"Yi Cloud Dome 1080p"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Cloud Dome 1080p - Hi3518e chipset - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI OUTDOOR 1080P",
|
||||||
|
"Yi Outdoor 1080p"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Yi Outdoor 1080p - Hi3518e chipset - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"YI OUTDOOR 1080P",
|
||||||
|
"Yi Outdoor 1080p"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Yi Outdoor 1080p - Hi3518e chipset - SD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"HI3518E CHIPSET",
|
||||||
|
"Generic Hi3518e",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Generic Yi camera with Hi3518e chipset - HD stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"HI3518E CHIPSET",
|
||||||
|
"Generic Hi3518e",
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Generic Yi camera with Hi3518e chipset - SD stream"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
{
|
||||||
|
"brand": "yi-hack-v5",
|
||||||
|
"brand_id": "yi-hack-v5",
|
||||||
|
"last_updated": "2025-11-11",
|
||||||
|
"source": "github.com/alienatedsec/yi-hack-v5",
|
||||||
|
"website": "https://github.com/alienatedsec/yi-hack-v5",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"HI3518EV200 CHIPSET",
|
||||||
|
"Yi Home",
|
||||||
|
"Yi Dome",
|
||||||
|
"Generic Hi3518ev200"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Hi3518ev200 chipset - HD video stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"HI3518EV200 CHIPSET",
|
||||||
|
"Yi Home",
|
||||||
|
"Yi Dome",
|
||||||
|
"Generic Hi3518ev200"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Hi3518ev200 chipset - SD video stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"HI3518EV200 CHIPSET",
|
||||||
|
"Yi Home",
|
||||||
|
"Yi Dome",
|
||||||
|
"Generic Hi3518ev200"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_2.h264",
|
||||||
|
"notes": "Hi3518ev200 chipset - Audio only stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"HI3518EV200 CHIPSET",
|
||||||
|
"Yi Home",
|
||||||
|
"Yi Dome",
|
||||||
|
"Generic Hi3518ev200"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_3.h264",
|
||||||
|
"notes": "Hi3518ev200 chipset - Audio only stream (alternative)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_0.h264",
|
||||||
|
"notes": "Generic - HD video stream"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
|
"type": "FFMPEG",
|
||||||
|
"protocol": "rtsp",
|
||||||
|
"port": 554,
|
||||||
|
"url": "/ch0_1.h264",
|
||||||
|
"notes": "Generic - SD video stream"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -910,6 +910,83 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== FRIGATE TAB CUSTOM STYLES ===== */
|
||||||
|
.frigate-input-section {
|
||||||
|
margin-bottom: var(--space-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.frigate-label {
|
||||||
|
display: block;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: var(--space-2);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.frigate-label .hint {
|
||||||
|
display: block;
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
margin-top: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.frigate-config-input {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 300px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
color: var(--text-primary);
|
||||||
|
resize: vertical;
|
||||||
|
transition: all var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.frigate-config-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--border-focus);
|
||||||
|
box-shadow: 0 0 0 3px var(--purple-glow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-generate {
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--space-4) var(--space-6);
|
||||||
|
font-size: var(--text-lg);
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: var(--space-6);
|
||||||
|
background: var(--purple-primary);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all var(--transition-fast);
|
||||||
|
box-shadow: 0 4px 12px var(--purple-glow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-generate:hover {
|
||||||
|
background: var(--purple-light);
|
||||||
|
box-shadow: 0 8px 24px var(--purple-glow-strong);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-generate:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.frigate-output-section {
|
||||||
|
margin-top: var(--space-6);
|
||||||
|
padding-top: var(--space-6);
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
animation: slideIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frigate-output-section.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* ===== UTILITIES ===== */
|
/* ===== UTILITIES ===== */
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
|
|||||||
@@ -1,115 +1,365 @@
|
|||||||
/**
|
/**
|
||||||
* Frigate NVR Configuration Generator
|
* Frigate NVR Configuration Generator
|
||||||
* Generates unified Frigate + Go2RTC YAML configs
|
* Adds cameras to existing Frigate configuration
|
||||||
* All cameras are routed through Frigate's built-in go2rtc for optimal performance
|
* Based on frigate-conf-generate logic
|
||||||
*/
|
*/
|
||||||
export class FrigateGenerator {
|
export class FrigateGenerator {
|
||||||
/**
|
/**
|
||||||
* Generate complete Frigate config with embedded Go2RTC
|
* Main entry point - generates config (new or adds to existing)
|
||||||
* @param {Object} mainStream - Main stream object (used for recording)
|
* @param {string} existingConfig - Existing YAML config text (or empty string)
|
||||||
* @param {Object} subStream - Optional sub stream object (used for detection if provided)
|
* @param {Object} mainStream - Main stream object
|
||||||
|
* @param {Object} subStream - Optional sub stream object
|
||||||
* @returns {string} YAML configuration string
|
* @returns {string} YAML configuration string
|
||||||
*/
|
*/
|
||||||
static generate(mainStream, subStream = null) {
|
static generate(existingConfig, mainStream, subStream = null) {
|
||||||
const cameraName = this.generateCameraName(mainStream);
|
if (!existingConfig || existingConfig.trim() === '') {
|
||||||
const config = [];
|
// Create new config from scratch
|
||||||
|
return this.createNewConfig(mainStream, subStream);
|
||||||
// MQTT Configuration
|
|
||||||
config.push('mqtt:');
|
|
||||||
config.push(' enabled: false');
|
|
||||||
config.push('');
|
|
||||||
|
|
||||||
// Global Record Configuration
|
|
||||||
config.push('# Global Recording Settings');
|
|
||||||
config.push('record:');
|
|
||||||
config.push(' enabled: true');
|
|
||||||
config.push(' retain:');
|
|
||||||
config.push(' days: 7');
|
|
||||||
config.push(' mode: motion # Record only on motion detection');
|
|
||||||
config.push('');
|
|
||||||
|
|
||||||
// Generate Go2RTC section
|
|
||||||
config.push('# Go2RTC Configuration (Frigate built-in)');
|
|
||||||
config.push('go2rtc:');
|
|
||||||
config.push(' streams:');
|
|
||||||
|
|
||||||
// Main stream configuration
|
|
||||||
const mainStreamName = this.generateStreamName(mainStream, 'main');
|
|
||||||
const mainSource = this.generateGo2RTCSource(mainStream);
|
|
||||||
config.push(` '${mainStreamName}':`);
|
|
||||||
config.push(` - ${mainSource}`);
|
|
||||||
|
|
||||||
// Sub stream configuration if provided
|
|
||||||
if (subStream) {
|
|
||||||
config.push('');
|
|
||||||
const subStreamName = this.generateStreamName(subStream, 'sub');
|
|
||||||
const subSource = this.generateGo2RTCSource(subStream);
|
|
||||||
config.push(` '${subStreamName}':`);
|
|
||||||
config.push(` - ${subSource}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config.push('');
|
// Add to existing config
|
||||||
|
return this.addToExistingConfig(existingConfig, mainStream, subStream);
|
||||||
|
}
|
||||||
|
|
||||||
// Generate Frigate cameras section
|
/**
|
||||||
config.push('# Frigate Camera Configuration');
|
* Add camera to existing config (text-based, preserves everything)
|
||||||
config.push('cameras:');
|
*/
|
||||||
config.push(` ${cameraName}:`);
|
static addToExistingConfig(existingConfig, mainStream, subStream) {
|
||||||
config.push(' ffmpeg:');
|
const lines = existingConfig.split('\n');
|
||||||
config.push(' inputs:');
|
|
||||||
|
|
||||||
|
// Find existing camera names and stream names to avoid duplicates
|
||||||
|
const existingCameras = this.findExistingCameras(lines);
|
||||||
|
const existingStreams = this.findExistingStreams(lines);
|
||||||
|
|
||||||
|
// Generate unique camera info
|
||||||
|
const cameraInfo = this.generateUniqueCameraInfo(mainStream, subStream, existingCameras, existingStreams);
|
||||||
|
|
||||||
|
// Find insertion points
|
||||||
|
const go2rtcStreamIndex = this.findGo2rtcStreamsInsertionPoint(lines);
|
||||||
|
const camerasInsertIndex = this.findCamerasInsertionPoint(lines);
|
||||||
|
|
||||||
|
if (go2rtcStreamIndex === -1 || camerasInsertIndex === -1) {
|
||||||
|
throw new Error('Could not find go2rtc streams or cameras section in config');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate new stream lines
|
||||||
|
const streamLines = this.generateStreamLines(cameraInfo);
|
||||||
|
|
||||||
|
// Generate new camera lines
|
||||||
|
const cameraLines = this.generateCameraLines(cameraInfo);
|
||||||
|
|
||||||
|
// Insert streams into go2rtc section
|
||||||
|
lines.splice(go2rtcStreamIndex, 0, ...streamLines);
|
||||||
|
|
||||||
|
// Insert camera into cameras section (adjust index after first insertion)
|
||||||
|
const adjustedCameraIndex = camerasInsertIndex + streamLines.length;
|
||||||
|
lines.splice(adjustedCameraIndex, 0, ...cameraLines);
|
||||||
|
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find existing camera names
|
||||||
|
*/
|
||||||
|
static findExistingCameras(lines) {
|
||||||
|
const cameras = new Set();
|
||||||
|
let inCamerasSection = false;
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.match(/^cameras:/)) {
|
||||||
|
inCamerasSection = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inCamerasSection && line.match(/^[a-z]/)) {
|
||||||
|
break; // Next top-level section
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inCamerasSection && line.match(/^\s{2}(\w+):/)) {
|
||||||
|
const match = line.match(/^\s{2}(\w+):/);
|
||||||
|
cameras.add(match[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cameras;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find existing stream names
|
||||||
|
*/
|
||||||
|
static findExistingStreams(lines) {
|
||||||
|
const streams = new Set();
|
||||||
|
let inStreamsSection = false;
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.match(/^\s{2}streams:/)) {
|
||||||
|
inStreamsSection = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inStreamsSection && line.match(/^[a-z]/)) {
|
||||||
|
break; // Next top-level section
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inStreamsSection && line.match(/^\s{4}'?(\w+)'?:/)) {
|
||||||
|
const match = line.match(/^\s{4}'?(\w+)'?:/);
|
||||||
|
streams.add(match[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return streams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find where to insert new streams in go2rtc section
|
||||||
|
*/
|
||||||
|
static findGo2rtcStreamsInsertionPoint(lines) {
|
||||||
|
let inStreamsSection = false;
|
||||||
|
let lastStreamIndex = -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
|
||||||
|
if (line.match(/^\s{2}streams:/)) {
|
||||||
|
inStreamsSection = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inStreamsSection) {
|
||||||
|
// Check if this is a stream definition or its content
|
||||||
|
if (line.match(/^\s{4,}/)) {
|
||||||
|
lastStreamIndex = i;
|
||||||
|
} else if (line.match(/^[a-z#]/)) {
|
||||||
|
// Found next section - insert before empty line if it exists
|
||||||
|
if (lastStreamIndex >= 0 && lines[lastStreamIndex + 1]?.trim() === '') {
|
||||||
|
return lastStreamIndex + 2; // After existing empty line
|
||||||
|
}
|
||||||
|
return lastStreamIndex + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastStreamIndex + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find where to insert new camera in cameras section
|
||||||
|
*/
|
||||||
|
static findCamerasInsertionPoint(lines) {
|
||||||
|
let inCamerasSection = false;
|
||||||
|
let lastCameraLineIndex = -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
|
||||||
|
if (line.match(/^cameras:/)) {
|
||||||
|
inCamerasSection = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inCamerasSection) {
|
||||||
|
// Check if we're still in a camera definition
|
||||||
|
if (line.match(/^\s{2}\w+:/)) {
|
||||||
|
// New camera starting
|
||||||
|
lastCameraLineIndex = i;
|
||||||
|
} else if (line.match(/^\s{2,}\S/)) {
|
||||||
|
// Still inside camera definition
|
||||||
|
lastCameraLineIndex = i;
|
||||||
|
} else if (line.match(/^[a-z]/) && !line.match(/^cameras:/)) {
|
||||||
|
// Found next top-level section
|
||||||
|
// Skip any empty lines before it
|
||||||
|
let insertIndex = lastCameraLineIndex + 1;
|
||||||
|
while (insertIndex < lines.length && lines[insertIndex].trim() === '') {
|
||||||
|
insertIndex++;
|
||||||
|
}
|
||||||
|
return insertIndex;
|
||||||
|
} else if (line.match(/^version:/)) {
|
||||||
|
// Insert before version, skip empty lines
|
||||||
|
let insertIndex = i;
|
||||||
|
while (insertIndex > 0 && lines[insertIndex - 1].trim() === '') {
|
||||||
|
insertIndex--;
|
||||||
|
}
|
||||||
|
return insertIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach end of file, insert at end
|
||||||
|
return lines.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate unique camera info avoiding duplicates
|
||||||
|
*/
|
||||||
|
static generateUniqueCameraInfo(mainStream, subStream, existingCameras, existingStreams) {
|
||||||
|
const ip = this.extractIP(mainStream.url);
|
||||||
|
const baseName = ip ? `camera_${ip.replace(/\./g, '_').replace(/:/g, '_')}` : 'camera';
|
||||||
|
const streamBaseName = ip ? ip.replace(/\./g, '_').replace(/:/g, '_') : 'stream';
|
||||||
|
|
||||||
|
// Find unique camera name
|
||||||
|
let cameraName = baseName;
|
||||||
|
let suffix = 0;
|
||||||
|
while (existingCameras.has(cameraName)) {
|
||||||
|
suffix++;
|
||||||
|
cameraName = `${baseName}_${suffix}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find unique stream names
|
||||||
|
let mainStreamName = `${streamBaseName}_main${suffix ? `_${suffix}` : ''}`;
|
||||||
|
while (existingStreams.has(mainStreamName)) {
|
||||||
|
suffix++;
|
||||||
|
mainStreamName = `${streamBaseName}_main_${suffix}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let subStreamName = null;
|
||||||
if (subStream) {
|
if (subStream) {
|
||||||
// If sub stream exists: use it for detection, main for recording
|
subStreamName = `${streamBaseName}_sub${suffix ? `_${suffix}` : ''}`;
|
||||||
const subStreamName = this.generateStreamName(subStream, 'sub');
|
while (existingStreams.has(subStreamName)) {
|
||||||
config.push(` - path: rtsp://127.0.0.1:8554/${subStreamName}`);
|
suffix++;
|
||||||
config.push(' input_args: preset-rtsp-restream');
|
subStreamName = `${streamBaseName}_sub_${suffix}`;
|
||||||
config.push(' roles:');
|
}
|
||||||
config.push(' - detect');
|
}
|
||||||
config.push(` - path: rtsp://127.0.0.1:8554/${mainStreamName}`);
|
|
||||||
config.push(' input_args: preset-rtsp-restream');
|
return {
|
||||||
config.push(' roles:');
|
cameraName,
|
||||||
config.push(' - record');
|
mainStreamName,
|
||||||
|
subStreamName,
|
||||||
|
mainStream,
|
||||||
|
subStream
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate stream lines for go2rtc section
|
||||||
|
*/
|
||||||
|
static generateStreamLines(cameraInfo) {
|
||||||
|
const lines = [];
|
||||||
|
|
||||||
|
// Add main stream
|
||||||
|
const mainSource = this.generateGo2RTCSource(cameraInfo.mainStream);
|
||||||
|
lines.push(` '${cameraInfo.mainStreamName}':`);
|
||||||
|
lines.push(` - ${mainSource}`);
|
||||||
|
|
||||||
|
// Add sub stream if provided
|
||||||
|
if (cameraInfo.subStream) {
|
||||||
|
lines.push('');
|
||||||
|
const subSource = this.generateGo2RTCSource(cameraInfo.subStream);
|
||||||
|
lines.push(` '${cameraInfo.subStreamName}':`);
|
||||||
|
lines.push(` - ${subSource}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push('');
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate camera lines for cameras section
|
||||||
|
*/
|
||||||
|
static generateCameraLines(cameraInfo) {
|
||||||
|
const lines = [];
|
||||||
|
|
||||||
|
lines.push(` ${cameraInfo.cameraName}:`);
|
||||||
|
lines.push(' ffmpeg:');
|
||||||
|
lines.push(' inputs:');
|
||||||
|
|
||||||
|
if (cameraInfo.subStream) {
|
||||||
|
// Use sub for detect, main for record
|
||||||
|
lines.push(` - path: rtsp://127.0.0.1:8554/${cameraInfo.subStreamName}`);
|
||||||
|
lines.push(' input_args: preset-rtsp-restream');
|
||||||
|
lines.push(' roles:');
|
||||||
|
lines.push(' - detect');
|
||||||
|
lines.push(` - path: rtsp://127.0.0.1:8554/${cameraInfo.mainStreamName}`);
|
||||||
|
lines.push(' input_args: preset-rtsp-restream');
|
||||||
|
lines.push(' roles:');
|
||||||
|
lines.push(' - record');
|
||||||
|
|
||||||
|
// Add live view configuration
|
||||||
|
lines.push(' live:');
|
||||||
|
lines.push(' streams:');
|
||||||
|
lines.push(` Main Stream: ${cameraInfo.mainStreamName} # HD для просмотра`);
|
||||||
|
lines.push(` Sub Stream: ${cameraInfo.subStreamName} # Низкое разрешение (опционально)`);
|
||||||
} else {
|
} else {
|
||||||
// No sub stream: use main for both detection and recording
|
// Use main for both detect and record
|
||||||
config.push(` - path: rtsp://127.0.0.1:8554/${mainStreamName}`);
|
lines.push(` - path: rtsp://127.0.0.1:8554/${cameraInfo.mainStreamName}`);
|
||||||
config.push(' input_args: preset-rtsp-restream');
|
lines.push(' input_args: preset-rtsp-restream');
|
||||||
config.push(' roles:');
|
lines.push(' roles:');
|
||||||
config.push(' - detect');
|
lines.push(' - detect');
|
||||||
config.push(' - record');
|
lines.push(' - record');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Live view configuration
|
// Add objects configuration
|
||||||
if (subStream) {
|
lines.push(' objects:');
|
||||||
config.push(' live:');
|
lines.push(' track:');
|
||||||
config.push(' streams:');
|
lines.push(' - person');
|
||||||
config.push(` Main Stream: ${mainStreamName} # HD для просмотра`);
|
lines.push(' - car');
|
||||||
config.push(` Sub Stream: ${this.generateStreamName(subStream, 'sub')} # Низкое разрешение (опционально)`);
|
lines.push(' - cat');
|
||||||
|
lines.push(' - dog');
|
||||||
|
|
||||||
|
// Add record configuration
|
||||||
|
lines.push(' record:');
|
||||||
|
lines.push(' enabled: true');
|
||||||
|
lines.push('');
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new configuration from scratch
|
||||||
|
*/
|
||||||
|
static createNewConfig(mainStream, subStream) {
|
||||||
|
const cameraInfo = this.generateUniqueCameraInfo(mainStream, subStream, new Set(), new Set());
|
||||||
|
const lines = [];
|
||||||
|
|
||||||
|
// MQTT
|
||||||
|
lines.push('mqtt:');
|
||||||
|
lines.push(' enabled: false');
|
||||||
|
lines.push('');
|
||||||
|
|
||||||
|
// Record
|
||||||
|
lines.push('# Global Recording Settings');
|
||||||
|
lines.push('record:');
|
||||||
|
lines.push(' enabled: true');
|
||||||
|
lines.push(' retain:');
|
||||||
|
lines.push(' days: 7');
|
||||||
|
lines.push(' mode: motion # Record only on motion detection');
|
||||||
|
lines.push('');
|
||||||
|
|
||||||
|
// Go2RTC
|
||||||
|
lines.push('# Go2RTC Configuration (Frigate built-in)');
|
||||||
|
lines.push('go2rtc:');
|
||||||
|
lines.push(' streams:');
|
||||||
|
lines.push(...this.generateStreamLines(cameraInfo));
|
||||||
|
|
||||||
|
// Cameras
|
||||||
|
lines.push('# Frigate Camera Configuration');
|
||||||
|
lines.push('cameras:');
|
||||||
|
lines.push(...this.generateCameraLines(cameraInfo));
|
||||||
|
|
||||||
|
// Version
|
||||||
|
lines.push('version: 0.16-0');
|
||||||
|
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract IP address from URL
|
||||||
|
*/
|
||||||
|
static extractIP(url) {
|
||||||
|
try {
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
return urlObj.hostname;
|
||||||
|
} catch (e) {
|
||||||
|
// Try to extract IP with regex
|
||||||
|
const match = url.match(/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/);
|
||||||
|
return match ? match[1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object detection configuration
|
|
||||||
config.push(' objects:');
|
|
||||||
config.push(' track:');
|
|
||||||
config.push(' - person');
|
|
||||||
config.push(' - car');
|
|
||||||
config.push(' - cat');
|
|
||||||
config.push(' - dog');
|
|
||||||
|
|
||||||
// Recording configuration
|
|
||||||
config.push(' record:');
|
|
||||||
config.push(' enabled: true');
|
|
||||||
|
|
||||||
config.push('');
|
|
||||||
config.push('version: 0.16-0');
|
|
||||||
|
|
||||||
return config.join('\n');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate Go2RTC source configuration based on stream type
|
* Generate Go2RTC source configuration based on stream type
|
||||||
* Returns the source string for go2rtc streams section
|
|
||||||
*/
|
*/
|
||||||
static generateGo2RTCSource(stream) {
|
static generateGo2RTCSource(stream) {
|
||||||
// Handle JPEG snapshots with exec:ffmpeg conversion
|
// Handle JPEG snapshots with exec:ffmpeg conversion
|
||||||
// Uses full path to ffmpeg and {{output}} for Frigate template escaping
|
|
||||||
if (stream.type === 'JPEG') {
|
if (stream.type === 'JPEG') {
|
||||||
return [
|
return [
|
||||||
'exec:/usr/lib/ffmpeg/7.0/bin/ffmpeg',
|
'exec:/usr/lib/ffmpeg/7.0/bin/ffmpeg',
|
||||||
@@ -122,7 +372,7 @@ export class FrigateGenerator {
|
|||||||
'-preset ultrafast',
|
'-preset ultrafast',
|
||||||
'-tune zerolatency',
|
'-tune zerolatency',
|
||||||
'-g 20',
|
'-g 20',
|
||||||
'-f rtsp {{output}}' // Double braces for Frigate template escaping
|
'-f rtsp {{output}}'
|
||||||
].join(' ');
|
].join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,16 +380,12 @@ export class FrigateGenerator {
|
|||||||
if (stream.type === 'ONVIF') {
|
if (stream.type === 'ONVIF') {
|
||||||
try {
|
try {
|
||||||
const urlObj = new URL(stream.url);
|
const urlObj = new URL(stream.url);
|
||||||
// Extract credentials and host from HTTP URL
|
|
||||||
const username = urlObj.username || 'admin';
|
const username = urlObj.username || 'admin';
|
||||||
const password = urlObj.password || '';
|
const password = urlObj.password || '';
|
||||||
const host = urlObj.hostname;
|
const host = urlObj.hostname;
|
||||||
const port = urlObj.port || '80';
|
const port = urlObj.port || '80';
|
||||||
|
|
||||||
// Generate onvif:// URL
|
|
||||||
return `onvif://${username}:${password}@${host}:${port}`;
|
return `onvif://${username}:${password}@${host}:${port}`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// If URL parsing fails, return as-is
|
|
||||||
return stream.url;
|
return stream.url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,36 +405,7 @@ export class FrigateGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all other types (RTSP, MJPEG, HLS, HTTP-FLV, RTMP, etc.): use direct URL
|
// For all other types: use direct URL
|
||||||
// Go2RTC handles these formats natively
|
|
||||||
return stream.url;
|
return stream.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate camera name from IP address
|
|
||||||
* Format: "camera_192_168_1_100"
|
|
||||||
*/
|
|
||||||
static generateCameraName(stream) {
|
|
||||||
try {
|
|
||||||
const urlObj = new URL(stream.url);
|
|
||||||
const ip = urlObj.hostname.replace(/\./g, '_').replace(/:/g, '_');
|
|
||||||
return `camera_${ip}`;
|
|
||||||
} catch (e) {
|
|
||||||
return 'camera';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate stream name for Go2RTC reference
|
|
||||||
* Format: "192_168_1_100_main" or "192_168_1_100_sub"
|
|
||||||
*/
|
|
||||||
static generateStreamName(stream, suffix) {
|
|
||||||
try {
|
|
||||||
const urlObj = new URL(stream.url);
|
|
||||||
const ip = urlObj.hostname.replace(/\./g, '_').replace(/:/g, '_');
|
|
||||||
return `${ip}_${suffix}`;
|
|
||||||
} catch (e) {
|
|
||||||
return `camera_stream_${suffix}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { StreamDiscoveryAPI } from './api/stream-discovery.js';
|
|||||||
import { SearchForm } from './ui/search-form.js';
|
import { SearchForm } from './ui/search-form.js';
|
||||||
import { StreamCarousel } from './ui/stream-carousel.js';
|
import { StreamCarousel } from './ui/stream-carousel.js';
|
||||||
import { ConfigPanel } from './ui/config-panel.js';
|
import { ConfigPanel } from './ui/config-panel.js';
|
||||||
|
import { FrigateGenerator } from './config-generators/frigate/index.js';
|
||||||
import { showToast } from './utils/toast.js';
|
import { showToast } from './utils/toast.js';
|
||||||
|
|
||||||
class StrixApp {
|
class StrixApp {
|
||||||
@@ -106,6 +107,9 @@ class StrixApp {
|
|||||||
document.getElementById('btn-add-sub-stream').addEventListener('click', () => this.addSubStream());
|
document.getElementById('btn-add-sub-stream').addEventListener('click', () => this.addSubStream());
|
||||||
document.getElementById('btn-remove-sub').addEventListener('click', () => this.removeSubStream());
|
document.getElementById('btn-remove-sub').addEventListener('click', () => this.removeSubStream());
|
||||||
|
|
||||||
|
// Frigate config generation
|
||||||
|
document.getElementById('btn-generate-frigate').addEventListener('click', () => this.generateFrigateConfig());
|
||||||
|
|
||||||
document.getElementById('btn-new-search').addEventListener('click', () => {
|
document.getElementById('btn-new-search').addEventListener('click', () => {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.showScreen('address');
|
this.showScreen('address');
|
||||||
@@ -354,6 +358,11 @@ class StrixApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.isSelectingSubStream = true;
|
this.isSelectingSubStream = true;
|
||||||
|
|
||||||
|
// Clear Frigate output section (but NOT the user's input textarea)
|
||||||
|
document.getElementById('frigate-output-section').classList.add('hidden');
|
||||||
|
document.getElementById('config-frigate').textContent = '';
|
||||||
|
|
||||||
showToast('Select a sub stream from available streams');
|
showToast('Select a sub stream from available streams');
|
||||||
this.showScreen('discovery');
|
this.showScreen('discovery');
|
||||||
}
|
}
|
||||||
@@ -365,6 +374,40 @@ class StrixApp {
|
|||||||
showToast('Sub stream removed');
|
showToast('Sub stream removed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate Frigate config by adding camera to existing config
|
||||||
|
*/
|
||||||
|
generateFrigateConfig() {
|
||||||
|
const existingConfig = document.getElementById('existing-frigate-config').value;
|
||||||
|
const mainStream = this.selectedMainStream;
|
||||||
|
const subStream = this.selectedSubStream;
|
||||||
|
|
||||||
|
if (!mainStream) {
|
||||||
|
showToast('No main stream selected', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Generate config using FrigateGenerator
|
||||||
|
const newConfig = FrigateGenerator.generate(existingConfig, mainStream, subStream);
|
||||||
|
|
||||||
|
// Show result
|
||||||
|
document.getElementById('config-frigate').textContent = newConfig;
|
||||||
|
document.getElementById('frigate-output-section').classList.remove('hidden');
|
||||||
|
|
||||||
|
// Scroll to result
|
||||||
|
document.getElementById('frigate-output-section').scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'nearest'
|
||||||
|
});
|
||||||
|
|
||||||
|
showToast('Config generated successfully!');
|
||||||
|
} catch (error) {
|
||||||
|
showToast(`Error: ${error.message}`, 'error');
|
||||||
|
console.error('Config generation error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateSubStreamUI() {
|
updateSubStreamUI() {
|
||||||
const subStreamInfo = document.getElementById('sub-stream-info');
|
const subStreamInfo = document.getElementById('sub-stream-info');
|
||||||
const addSubStreamBtn = document.getElementById('btn-add-sub-stream');
|
const addSubStreamBtn = document.getElementById('btn-add-sub-stream');
|
||||||
|
|||||||
@@ -21,15 +21,59 @@ export class ConfigPanel {
|
|||||||
document.getElementById('selected-sub-url').textContent = this.maskCredentials(subStream.url);
|
document.getElementById('selected-sub-url').textContent = this.maskCredentials(subStream.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate configs
|
// Generate configs for URL and Go2RTC (as before)
|
||||||
const urlConfig = this.generateURLConfig();
|
const urlConfig = this.generateURLConfig();
|
||||||
const go2rtcConfig = Go2RTCGenerator.generate(mainStream, subStream);
|
const go2rtcConfig = Go2RTCGenerator.generate(mainStream, subStream);
|
||||||
const frigateConfig = FrigateGenerator.generate(mainStream, subStream);
|
|
||||||
|
|
||||||
// Update config displays
|
// Update config displays
|
||||||
document.getElementById('config-url').textContent = urlConfig;
|
document.getElementById('config-url').textContent = urlConfig;
|
||||||
document.getElementById('config-go2rtc').textContent = go2rtcConfig;
|
document.getElementById('config-go2rtc').textContent = go2rtcConfig;
|
||||||
document.getElementById('config-frigate').textContent = frigateConfig;
|
|
||||||
|
// For Frigate: initialize the tab instead of generating automatically
|
||||||
|
this.initializeFrigateTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize Frigate tab with example config
|
||||||
|
*/
|
||||||
|
initializeFrigateTab() {
|
||||||
|
const textarea = document.getElementById('existing-frigate-config');
|
||||||
|
const outputSection = document.getElementById('frigate-output-section');
|
||||||
|
|
||||||
|
// Show example config if field is empty
|
||||||
|
if (!textarea.value || textarea.value.trim() === '') {
|
||||||
|
textarea.value = this.getExampleConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide output section
|
||||||
|
outputSection.classList.add('hidden');
|
||||||
|
document.getElementById('config-frigate').textContent = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get example Frigate config
|
||||||
|
*/
|
||||||
|
getExampleConfig() {
|
||||||
|
return `mqtt:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Global Recording Settings
|
||||||
|
record:
|
||||||
|
enabled: true
|
||||||
|
retain:
|
||||||
|
days: 7
|
||||||
|
mode: motion # Record only on motion detection
|
||||||
|
|
||||||
|
# Go2RTC Configuration (Frigate built-in)
|
||||||
|
go2rtc:
|
||||||
|
streams:
|
||||||
|
# Your existing streams will be preserved here
|
||||||
|
|
||||||
|
# Frigate Camera Configuration
|
||||||
|
cameras:
|
||||||
|
# Your existing cameras will be preserved here
|
||||||
|
|
||||||
|
version: 0.16-0`;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateURLConfig() {
|
generateURLConfig() {
|
||||||
|
|||||||
Reference in New Issue
Block a user