2026-10-04 04:52:39 +08:00
'use strict' ;
let cameraSettingsContext = null , cameraSettingsGeneration = 0 ;
const videoLabels = { resolution : '分辨率' , fps : '帧率(帧/秒)' , bitrate : '码率上限(kbps) ' , quality : '画质等级' , gop : '关键帧间隔(帧)' };
function clearCameraSettings (){ cameraSettingsGeneration ++ ; cameraSettingsContext = null ; for ( const id of [ 'camera-settings-info' , 'camera-image-fields' , 'camera-video-fields' , 'camera-settings-status' ]) $ ( '#' + id ). replaceChildren ();}
function settingsInput ( key , label , value , rule ){
const enumLabel = v => key === 'dayNight' ? ({ AUTO : '自动日夜' , ON : '日间(红外截止)' , OFF : '夜间(红外通过)' })[ v ] || v : ({ ON : '开启' , OFF : '关闭' , AUTO : '自动' , MANUAL : '手动' })[ v ] || v ;
return `<label> ${ esc ( label ) }${ rule . choices ? `<select name=" ${ esc ( key ) } "> ${ rule . choices . map ( v => `<option value=" ${ esc ( v ) } " ${ v === value ? 'selected' : '' } > ${ esc ( enumLabel ( v )) } </option>` ). join ( '' ) } </select>` : `<input name=" ${ esc ( key ) } " type="number" value=" ${ esc ( value ) } " min=" ${ rule . min } " max=" ${ rule . max } " step=" ${ [ 'fps' , 'bitrate' , 'gop' ]. includes ( key ) ? '1' : 'any' } " required><small> ${ rule . min } – ${ rule . max } </small>` } </label>` ;
}
function renderCameraSettings ( data ){
const d = data . device ;
$ ( '#camera-settings-info' ). innerHTML = `<dl> ${ [[ '厂商' , d . Manufacturer ],[ '型号' , d . Model ],[ '固件' , d . FirmwareVersion ],[ '镜头视频源' , data . source ],[ '码流配置' , data . profile ],[ '设备报告编码' , data . video . codec ]]. map (([ k , v ])=> `<div><dt> ${ esc ( k ) } </dt><dd> ${ esc ( v || '未提供' ) } </dd></div>` ). join ( '' ) } </dl>` ;
$ ( '#camera-image-fields' ). innerHTML = Object . entries ( data . imaging ). map (([ k , r ])=> settingsInput ( k , r . label , r . value , r )). join ( '' ) || '<p class="description">设备未提供可调图像参数。</p>' ;
$ ( '#camera-video-fields' ). innerHTML = Object . entries ( data . video . options ). map (([ k , r ])=> settingsInput ( k , videoLabels [ k ], data . video . values [ k ], r )). join ( '' ) || '<p class="description">设备未提供可调码流参数。</p>' ;
$ ( '#camera-settings-status' ). textContent = data . notes . join ( '; ' );
$ ( '#camera-image-form button[type=submit]' ). disabled =! Object . keys ( data . imaging ). length ;
$ ( '#camera-video-form button[type=submit]' ). disabled =! Object . keys ( data . video . options ). length ;
}
async function loadCameraSettings (){
if ( ! cameraSettingsContext ) return ; const context = cameraSettingsContext , generation =++ cameraSettingsGeneration ;
context . settings = null ; context . quality = $ ( '#camera-settings-quality' ). value ;
$ ( '#camera-settings-status' ). textContent = '正在读取设备实际参数…' ;
2026-10-04 04:58:56 +08:00
$$ ( '#camera-settings-dialog form input,#camera-settings-dialog form select,#camera-settings-dialog form button[type=submit]' ). forEach ( b => b . disabled = true ); $ ( '#camera-settings-reload' ). disabled = true ;
2026-10-04 04:52:39 +08:00
try { const result = await api ( '/api/camera/settings?' + new URLSearchParams ({ camera : context . id , quality : context . quality }));
if ( generation !== cameraSettingsGeneration || context !== cameraSettingsContext ||! loggedIn ) return ; context . settings = result ; renderCameraSettings ( result );
} catch ( e ){ if ( ! e . stale && generation === cameraSettingsGeneration ) $ ( '#camera-settings-status' ). textContent = e . message ;}
finally { if ( generation === cameraSettingsGeneration ) $ ( '#camera-settings-reload' ). disabled = false ;}
}
async function openCameraSettings ( id ){
if ( ! isAdmin ()) return ; clearCameraSettings (); cameraSettingsContext = { id , settings : null , quality : 'main' };
$ ( '#camera-settings-title' ). textContent = ( state . cameras . find ( c => c . id === id ) ? . name || '摄像机' ) + ' · 参数设置' ; $ ( '#camera-settings-quality' ). value = 'main' ; $ ( '#camera-settings-quality' ). disabled = false ;
$ ( '#camera-settings-dialog' ). showModal (); await loadCameraSettings ();
}
function changedCameraValues ( form , section , settings ){
const rules = section === 'imaging' ? settings . imaging : settings . video . options , values = {};
for ( const [ key , rule ] of Object . entries ( rules )){
const value = rule . choices ? form . elements [ key ]. value : Number ( form . elements [ key ]. value );
if ( value !== ( section === 'imaging' ? rule . value : settings . video . values [ key ])) values [ key ] = value ;
}
return values ;
}
async function saveCameraSettings ( form , section ){
const context = cameraSettingsContext ; if ( ! context ? . settings ) return ;
const values = changedCameraValues ( form , section , context . settings ); if ( ! Object . keys ( values ). length ){ toast ( '参数没有变化' ); return ;}
const generation =++ cameraSettingsGeneration ;
2026-10-04 04:58:56 +08:00
$$ ( '#camera-settings-dialog form input,#camera-settings-dialog form select,#camera-settings-dialog form button[type=submit]' ). forEach ( b => b . disabled = true ); $ ( '#camera-settings-reload' ). disabled = true ; $ ( '#camera-settings-quality' ). disabled = true ;
2026-10-04 04:52:39 +08:00
$ ( '#camera-settings-status' ). textContent = '正在保存并读取设备返回值…' ;
try { const result = await api ( '/api/camera/settings' ,{ camera : context . id , quality : context . quality , revision : context . settings . revision , section , values });
if ( generation !== cameraSettingsGeneration || context !== cameraSettingsContext ||! loggedIn ) return ;
context . settings = result . settings ; if ( result . settings ) renderCameraSettings ( result . settings ); $ ( '#camera-settings-status' ). textContent = result . message ; toast ( result . message );
} catch ( e ){ if ( ! e . stale && generation === cameraSettingsGeneration ){ context . settings = null ; $ ( '#camera-settings-status' ). textContent = e . message + '。请点击“重新读取”核对设备状态。' ;}}
finally { if ( generation === cameraSettingsGeneration ){ $ ( '#camera-settings-reload' ). disabled = false ; $ ( '#camera-settings-quality' ). disabled = false ;}}
}
for ( const [ id , section ] of [[ 'camera-image-form' , 'imaging' ],[ 'camera-video-form' , 'video' ]]) $ ( '#' + id ). addEventListener ( 'submit' , e =>{ e . preventDefault (); saveCameraSettings ( e . currentTarget , section );});
$ ( '#camera-settings-reload' ). onclick = loadCameraSettings ; $ ( '#camera-settings-quality' ). onchange = loadCameraSettings ;
$ ( '#camera-settings-dialog' ). addEventListener ( 'close' , clearCameraSettings );
document . addEventListener ( 'click' , e =>{ const b = e . target . closest ( '[data-camera-settings]' ); if ( b ) openCameraSettings ( b . dataset . cameraSettings );});