Files
Strix/pkg/camdb/search.go
T
eduard256 27117900eb Rewrite Strix from scratch as single binary
Complete architecture rewrite following go2rtc patterns:
- pkg/ for pure logic (camdb, tester, probe, generate)
- internal/ for application glue with Init() modules
- Single HTTP server on :4567 with all endpoints
- zerolog with password masking and memory ring buffer
- Environment-based config only (no YAML files)

API endpoints: /api/search, /api/streams, /api/test,
/api/probe, /api/generate, /api/health, /api/log

Dependencies: go2rtc v1.9.14, go-sqlite3, miekg/dns, zerolog
2026-03-25 10:38:46 +00:00

138 lines
3.0 KiB
Go

package camdb
import (
"database/sql"
"strings"
)
type Result struct {
Type string `json:"type"`
ID string `json:"id"`
Name string `json:"name"`
}
// SearchAll returns all presets + all brands, no models
func SearchAll(db *sql.DB) ([]Result, error) {
var results []Result
rows, err := db.Query("SELECT preset_id, name FROM presets ORDER BY preset_id")
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var id, name string
if err = rows.Scan(&id, &name); err != nil {
return nil, err
}
results = append(results, Result{Type: "preset", ID: "p:" + id, Name: name})
}
rows, err = db.Query("SELECT brand_id, brand FROM brands ORDER BY brand LIMIT ?", 50-len(results))
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var id, name string
if err = rows.Scan(&id, &name); err != nil {
return nil, err
}
results = append(results, Result{Type: "brand", ID: "b:" + id, Name: name})
}
return results, nil
}
// SearchQuery searches presets, brands, models by query string (limit 50 total).
// Supports: "model", "brand model", "model brand" -- each word matches independently.
func SearchQuery(db *sql.DB, q string) ([]Result, error) {
var results []Result
like := "%" + q + "%"
// presets
rows, err := db.Query(
"SELECT preset_id, name FROM presets WHERE preset_id LIKE ? OR name LIKE ? ORDER BY preset_id",
like, like,
)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var id, name string
if err = rows.Scan(&id, &name); err != nil {
return nil, err
}
results = append(results, Result{Type: "preset", ID: "p:" + id, Name: name})
}
// brands
rows, err = db.Query(
"SELECT brand_id, brand FROM brands WHERE brand_id LIKE ? OR brand LIKE ? ORDER BY brand LIMIT ?",
like, like, 50-len(results),
)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var id, name string
if err = rows.Scan(&id, &name); err != nil {
return nil, err
}
results = append(results, Result{Type: "brand", ID: "b:" + id, Name: name})
}
if len(results) >= 50 {
return results, nil
}
// models -- each word must match brand or model
words := strings.Fields(q)
where := ""
args := make([]any, 0, len(words)+1)
for i, w := range words {
if i > 0 {
where += " AND "
}
where += "(b.brand LIKE ? OR b.brand_id LIKE ? OR sm.model LIKE ?)"
p := "%" + w + "%"
args = append(args, p, p, p)
}
args = append(args, 50-len(results))
rows, err = db.Query(
`SELECT DISTINCT b.brand_id, b.brand, sm.model
FROM stream_models sm
JOIN streams s ON s.id = sm.stream_id
JOIN brands b ON b.brand_id = s.brand_id
WHERE `+where+`
ORDER BY b.brand, sm.model
LIMIT ?`,
args...,
)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var brandID, brand, model string
if err = rows.Scan(&brandID, &brand, &model); err != nil {
return nil, err
}
results = append(results, Result{
Type: "model",
ID: "m:" + brandID + ":" + model,
Name: brand + ": " + model,
})
}
return results, nil
}