mirror of https://github.com/jetkvm/kvm.git
Compare commits
16 Commits
96227f1e8e
...
7397408052
| Author | SHA1 | Date |
|---|---|---|
|
|
7397408052 | |
|
|
336a75812f | |
|
|
ce4ea10551 | |
|
|
3448663afa | |
|
|
1d1e58f036 | |
|
|
09ddd21610 | |
|
|
10c4c959a8 | |
|
|
69b7682002 | |
|
|
92758c6337 | |
|
|
2cde2c0ecb | |
|
|
6eb842885d | |
|
|
bc1bc53fb2 | |
|
|
cdffb3e32a | |
|
|
362540d83d | |
|
|
57c9cf324d | |
|
|
a359cb34bd |
|
|
@ -37,7 +37,7 @@ jobs:
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
jetkvm-cgo-${{ hashFiles('internal/native/cgo/**/*.c', 'internal/native/cgo/**/*.h', 'internal/native/cgo/**/*.patch', 'internal/native/cgo/**/*.txt', 'internal/native/cgo/**/*.sh', '!internal/native/cgo/build/**') }}
|
jetkvm-cgo-${{ hashFiles('internal/native/cgo/**/*.c', 'internal/native/cgo/**/*.h', 'internal/native/cgo/**/*.patch', 'internal/native/cgo/**/*.txt', 'internal/native/cgo/**/*.sh', '!internal/native/cgo/build/**') }}
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: "22"
|
node-version: "22"
|
||||||
cache: "npm"
|
cache: "npm"
|
||||||
|
|
@ -63,7 +63,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
input: "testreport.json"
|
input: "testreport.json"
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: jetkvm-app
|
name: jetkvm-app
|
||||||
path: |
|
path: |
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
CI_HOST: ${{ vars.JETKVM_CI_HOST }}
|
CI_HOST: ${{ vars.JETKVM_CI_HOST }}
|
||||||
- name: Upload logs
|
- name: Upload logs
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: device-logs
|
name: device-logs
|
||||||
path: |
|
path: |
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: "22"
|
node-version: "22"
|
||||||
cache: "npm"
|
cache: "npm"
|
||||||
|
|
|
||||||
33
audio.go
33
audio.go
|
|
@ -21,7 +21,7 @@ var (
|
||||||
activeConnections atomic.Int32
|
activeConnections atomic.Int32
|
||||||
audioLogger zerolog.Logger
|
audioLogger zerolog.Logger
|
||||||
currentAudioTrack *webrtc.TrackLocalStaticSample
|
currentAudioTrack *webrtc.TrackLocalStaticSample
|
||||||
inputTrackHandling atomic.Bool
|
currentInputTrack atomic.Pointer[string]
|
||||||
audioOutputEnabled atomic.Bool
|
audioOutputEnabled atomic.Bool
|
||||||
audioInputEnabled atomic.Bool
|
audioInputEnabled atomic.Bool
|
||||||
)
|
)
|
||||||
|
|
@ -29,7 +29,8 @@ var (
|
||||||
func initAudio() {
|
func initAudio() {
|
||||||
audioLogger = logging.GetDefaultLogger().With().Str("component", "audio-manager").Logger()
|
audioLogger = logging.GetDefaultLogger().With().Str("component", "audio-manager").Logger()
|
||||||
|
|
||||||
audioOutputEnabled.Store(true)
|
ensureConfigLoaded()
|
||||||
|
audioOutputEnabled.Store(config.AudioOutputEnabled)
|
||||||
audioInputEnabled.Store(true)
|
audioInputEnabled.Store(true)
|
||||||
|
|
||||||
audioLogger.Debug().Msg("Audio subsystem initialized")
|
audioLogger.Debug().Msg("Audio subsystem initialized")
|
||||||
|
|
@ -151,13 +152,9 @@ func setAudioTrack(audioTrack *webrtc.TrackLocalStaticSample) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPendingInputTrack(track *webrtc.TrackRemote) {
|
func setPendingInputTrack(track *webrtc.TrackRemote) {
|
||||||
audioMutex.Lock()
|
trackID := track.ID()
|
||||||
defer audioMutex.Unlock()
|
currentInputTrack.Store(&trackID)
|
||||||
|
go handleInputTrackForSession(track)
|
||||||
// Start input track handler only once per WebRTC session
|
|
||||||
if inputTrackHandling.CompareAndSwap(false, true) {
|
|
||||||
go handleInputTrackForSession(track)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAudioOutputEnabled enables or disables audio output
|
// SetAudioOutputEnabled enables or disables audio output
|
||||||
|
|
@ -201,22 +198,32 @@ func SetAudioInputEnabled(enabled bool) error {
|
||||||
// handleInputTrackForSession runs for the entire WebRTC session lifetime
|
// handleInputTrackForSession runs for the entire WebRTC session lifetime
|
||||||
// It continuously reads from the track and sends to whatever relay is currently active
|
// It continuously reads from the track and sends to whatever relay is currently active
|
||||||
func handleInputTrackForSession(track *webrtc.TrackRemote) {
|
func handleInputTrackForSession(track *webrtc.TrackRemote) {
|
||||||
defer inputTrackHandling.Store(false)
|
myTrackID := track.ID()
|
||||||
|
|
||||||
audioLogger.Debug().
|
audioLogger.Debug().
|
||||||
Str("codec", track.Codec().MimeType).
|
Str("codec", track.Codec().MimeType).
|
||||||
Str("track_id", track.ID()).
|
Str("track_id", myTrackID).
|
||||||
Msg("starting session-lifetime track handler")
|
Msg("starting session-lifetime track handler")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
// Check if we've been superseded by a new track
|
||||||
|
currentTrackID := currentInputTrack.Load()
|
||||||
|
if currentTrackID != nil && *currentTrackID != myTrackID {
|
||||||
|
audioLogger.Debug().
|
||||||
|
Str("my_track_id", myTrackID).
|
||||||
|
Str("current_track_id", *currentTrackID).
|
||||||
|
Msg("audio track handler exiting - superseded by new track")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Read RTP packet (must always read to keep track alive)
|
// Read RTP packet (must always read to keep track alive)
|
||||||
rtpPacket, _, err := track.ReadRTP()
|
rtpPacket, _, err := track.ReadRTP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
audioLogger.Debug().Msg("audio track ended")
|
audioLogger.Debug().Str("track_id", myTrackID).Msg("audio track ended")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
audioLogger.Warn().Err(err).Msg("failed to read RTP packet")
|
audioLogger.Warn().Err(err).Str("track_id", myTrackID).Msg("failed to read RTP packet")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,8 @@ type Config struct {
|
||||||
DefaultLogLevel string `json:"default_log_level"`
|
DefaultLogLevel string `json:"default_log_level"`
|
||||||
VideoSleepAfterSec int `json:"video_sleep_after_sec"`
|
VideoSleepAfterSec int `json:"video_sleep_after_sec"`
|
||||||
VideoQualityFactor float64 `json:"video_quality_factor"`
|
VideoQualityFactor float64 `json:"video_quality_factor"`
|
||||||
|
AudioInputAutoEnable bool `json:"audio_input_auto_enable"`
|
||||||
|
AudioOutputEnabled bool `json:"audio_output_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetDisplayRotation() uint16 {
|
func (c *Config) GetDisplayRotation() uint16 {
|
||||||
|
|
@ -178,8 +180,10 @@ func getDefaultConfig() Config {
|
||||||
_ = confparser.SetDefaultsAndValidate(c)
|
_ = confparser.SetDefaultsAndValidate(c)
|
||||||
return c
|
return c
|
||||||
}(),
|
}(),
|
||||||
DefaultLogLevel: "INFO",
|
DefaultLogLevel: "INFO",
|
||||||
VideoQualityFactor: 1.0,
|
VideoQualityFactor: 1.0,
|
||||||
|
AudioInputAutoEnable: false,
|
||||||
|
AudioOutputEnabled: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
40
go.mod
40
go.mod
|
|
@ -4,26 +4,27 @@ go 1.24.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/semver/v3 v3.4.0
|
github.com/Masterminds/semver/v3 v3.4.0
|
||||||
github.com/beevik/ntp v1.4.3
|
github.com/beevik/ntp v1.5.0
|
||||||
github.com/coder/websocket v1.8.14
|
github.com/coder/websocket v1.8.14
|
||||||
github.com/coreos/go-oidc/v3 v3.15.0
|
github.com/coreos/go-oidc/v3 v3.16.0
|
||||||
github.com/creack/pty v1.1.24
|
github.com/creack/pty v1.1.24
|
||||||
github.com/erikdubbelboer/gspt v0.0.0-20210805194459-ce36a5128377
|
github.com/erikdubbelboer/gspt v0.0.0-20210805194459-ce36a5128377
|
||||||
github.com/fsnotify/fsnotify v1.9.0
|
github.com/fsnotify/fsnotify v1.9.0
|
||||||
github.com/gin-contrib/logger v1.2.6
|
github.com/gin-contrib/logger v1.2.6
|
||||||
github.com/gin-gonic/gin v1.10.1
|
github.com/gin-gonic/gin v1.10.1
|
||||||
github.com/go-co-op/gocron/v2 v2.16.6
|
github.com/go-co-op/gocron/v2 v2.17.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/guregu/null/v6 v6.0.0
|
github.com/guregu/null/v6 v6.0.0
|
||||||
github.com/gwatts/rootcerts v0.0.0-20250901182336-dc5ae18bd79f
|
github.com/gwatts/rootcerts v0.0.0-20250901182336-dc5ae18bd79f
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250919081422-f80a1952f48e
|
github.com/insomniacslk/dhcp v0.0.0-20250919081422-f80a1952f48e
|
||||||
|
github.com/mdlayher/ndp v1.1.0
|
||||||
github.com/pion/logging v0.2.4
|
github.com/pion/logging v0.2.4
|
||||||
github.com/pion/mdns/v2 v2.0.7
|
github.com/pion/mdns/v2 v2.0.7
|
||||||
github.com/pion/webrtc/v4 v4.1.4
|
github.com/pion/webrtc/v4 v4.1.6
|
||||||
github.com/pojntfx/go-nbd v0.3.2
|
github.com/pojntfx/go-nbd v0.3.2
|
||||||
github.com/prometheus/client_golang v1.23.2
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/prometheus/common v0.66.1
|
github.com/prometheus/common v0.67.2
|
||||||
github.com/prometheus/procfs v0.17.0
|
github.com/prometheus/procfs v0.19.2
|
||||||
github.com/psanford/httpreadat v0.1.0
|
github.com/psanford/httpreadat v0.1.0
|
||||||
github.com/rs/xid v1.6.0
|
github.com/rs/xid v1.6.0
|
||||||
github.com/rs/zerolog v1.34.0
|
github.com/rs/zerolog v1.34.0
|
||||||
|
|
@ -32,9 +33,9 @@ require (
|
||||||
github.com/vearutop/statigz v1.5.0
|
github.com/vearutop/statigz v1.5.0
|
||||||
github.com/vishvananda/netlink v1.3.1
|
github.com/vishvananda/netlink v1.3.1
|
||||||
go.bug.st/serial v1.6.4
|
go.bug.st/serial v1.6.4
|
||||||
golang.org/x/crypto v0.42.0
|
golang.org/x/crypto v0.43.0
|
||||||
golang.org/x/net v0.44.0
|
golang.org/x/net v0.46.0
|
||||||
golang.org/x/sys v0.36.0
|
golang.org/x/sys v0.37.0
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/pojntfx/go-nbd v0.3.2 => github.com/chemhack/go-nbd v0.0.0-20241006125820-59e45f5b1e7b
|
replace github.com/pojntfx/go-nbd v0.3.2 => github.com/chemhack/go-nbd v0.0.0-20241006125820-59e45f5b1e7b
|
||||||
|
|
@ -49,7 +50,7 @@ require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
|
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
||||||
|
|
@ -61,7 +62,6 @@ require (
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mdlayher/ndp v1.1.0 // indirect
|
|
||||||
github.com/mdlayher/packet v1.1.2 // indirect
|
github.com/mdlayher/packet v1.1.2 // indirect
|
||||||
github.com/mdlayher/socket v0.4.1 // indirect
|
github.com/mdlayher/socket v0.4.1 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
|
@ -73,15 +73,15 @@ require (
|
||||||
github.com/pion/datachannel v1.5.10 // indirect
|
github.com/pion/datachannel v1.5.10 // indirect
|
||||||
github.com/pion/dtls/v3 v3.0.7 // indirect
|
github.com/pion/dtls/v3 v3.0.7 // indirect
|
||||||
github.com/pion/ice/v4 v4.0.10 // indirect
|
github.com/pion/ice/v4 v4.0.10 // indirect
|
||||||
github.com/pion/interceptor v0.1.40 // indirect
|
github.com/pion/interceptor v0.1.41 // indirect
|
||||||
github.com/pion/randutil v0.1.0 // indirect
|
github.com/pion/randutil v0.1.0 // indirect
|
||||||
github.com/pion/rtcp v1.2.15 // indirect
|
github.com/pion/rtcp v1.2.15 // indirect
|
||||||
github.com/pion/rtp v1.8.22 // indirect
|
github.com/pion/rtp v1.8.23 // indirect
|
||||||
github.com/pion/sctp v1.8.39 // indirect
|
github.com/pion/sctp v1.8.40 // indirect
|
||||||
github.com/pion/sdp/v3 v3.0.16 // indirect
|
github.com/pion/sdp/v3 v3.0.16 // indirect
|
||||||
github.com/pion/srtp/v3 v3.0.7 // indirect
|
github.com/pion/srtp/v3 v3.0.8 // indirect
|
||||||
github.com/pion/stun/v3 v3.0.0 // indirect
|
github.com/pion/stun/v3 v3.0.0 // indirect
|
||||||
github.com/pion/transport/v3 v3.0.7 // indirect
|
github.com/pion/transport/v3 v3.0.8 // indirect
|
||||||
github.com/pion/turn/v4 v4.1.1 // indirect
|
github.com/pion/turn/v4 v4.1.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
|
|
@ -92,11 +92,11 @@ require (
|
||||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
github.com/vishvananda/netns v0.0.5 // indirect
|
github.com/vishvananda/netns v0.0.5 // indirect
|
||||||
github.com/wlynxg/anet v0.0.5 // indirect
|
github.com/wlynxg/anet v0.0.5 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
golang.org/x/arch v0.20.0 // indirect
|
golang.org/x/arch v0.20.0 // indirect
|
||||||
golang.org/x/oauth2 v0.30.0 // indirect
|
golang.org/x/oauth2 v0.32.0 // indirect
|
||||||
golang.org/x/sync v0.17.0 // indirect
|
golang.org/x/sync v0.17.0 // indirect
|
||||||
golang.org/x/text v0.29.0 // indirect
|
golang.org/x/text v0.30.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.9 // indirect
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
80
go.sum
80
go.sum
|
|
@ -2,8 +2,8 @@ github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1
|
||||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||||
github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho=
|
github.com/beevik/ntp v1.5.0 h1:y+uj/JjNwlY2JahivxYvtmv4ehfi3h74fAuABB9ZSM4=
|
||||||
github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q=
|
github.com/beevik/ntp v1.5.0/go.mod h1:mJEhBrwT76w9D+IfOEGvuzyuudiW9E52U2BaTrMOYow=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bool64/dev v0.2.39 h1:kP8DnMGlWXhGYJEZE/J0l/gVBdbuhoPGL+MJG4QbofE=
|
github.com/bool64/dev v0.2.39 h1:kP8DnMGlWXhGYJEZE/J0l/gVBdbuhoPGL+MJG4QbofE=
|
||||||
|
|
@ -20,8 +20,8 @@ github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI
|
||||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
|
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
|
||||||
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
||||||
github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg=
|
github.com/coreos/go-oidc/v3 v3.16.0 h1:qRQUCFstKpXwmEjDQTIbyY/5jF00+asXzSkmkoa/mow=
|
||||||
github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
github.com/coreos/go-oidc/v3 v3.16.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
|
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
|
||||||
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
||||||
|
|
@ -42,10 +42,10 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
|
||||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
github.com/go-co-op/gocron/v2 v2.16.6 h1:zI2Ya9sqvuLcgqJgV79LwoJXM8h20Z/drtB7ATbpRWo=
|
github.com/go-co-op/gocron/v2 v2.17.0 h1:e/oj6fcAM8vOOKZxv2Cgfmjo+s8AXC46po5ZPtaSea4=
|
||||||
github.com/go-co-op/gocron/v2 v2.16.6/go.mod h1:zAfC/GFQ668qHxOVl/D68Jh5Ce7sDqX6TJnSQyRkRBc=
|
github.com/go-co-op/gocron/v2 v2.17.0/go.mod h1:Zii6he+Zfgy5W9B+JKk/KwejFOW0kZTFvHtwIpR4aBI=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY=
|
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw=
|
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
|
@ -124,8 +124,8 @@ github.com/pion/dtls/v3 v3.0.7 h1:bItXtTYYhZwkPFk4t1n3Kkf5TDrfj6+4wG+CZR8uI9Q=
|
||||||
github.com/pion/dtls/v3 v3.0.7/go.mod h1:uDlH5VPrgOQIw59irKYkMudSFprY9IEFCqz/eTz16f8=
|
github.com/pion/dtls/v3 v3.0.7/go.mod h1:uDlH5VPrgOQIw59irKYkMudSFprY9IEFCqz/eTz16f8=
|
||||||
github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4=
|
github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4=
|
||||||
github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
|
github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
|
||||||
github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4=
|
github.com/pion/interceptor v0.1.41 h1:NpvX3HgWIukTf2yTBVjVGFXtpSpWgXjqz7IIpu7NsOw=
|
||||||
github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic=
|
github.com/pion/interceptor v0.1.41/go.mod h1:nEt4187unvRXJFyjiw00GKo+kIuXMWQI9K89fsosDLY=
|
||||||
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
|
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
|
||||||
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
|
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
|
||||||
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
||||||
|
|
@ -134,22 +134,22 @@ github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
|
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
|
||||||
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
|
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
|
||||||
github.com/pion/rtp v1.8.22 h1:8NCVDDF+uSJmMUkjLJVnIr/HX7gPesyMV1xFt5xozXc=
|
github.com/pion/rtp v1.8.23 h1:kxX3bN4nM97DPrVBGq5I/Xcl332HnTHeP1Swx3/MCnU=
|
||||||
github.com/pion/rtp v1.8.22/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM=
|
github.com/pion/rtp v1.8.23/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM=
|
||||||
github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE=
|
github.com/pion/sctp v1.8.40 h1:bqbgWYOrUhsYItEnRObUYZuzvOMsVplS3oNgzedBlG8=
|
||||||
github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE=
|
github.com/pion/sctp v1.8.40/go.mod h1:SPBBUENXE6ThkEksN5ZavfAhFYll+h+66ZiG6IZQuzo=
|
||||||
github.com/pion/sdp/v3 v3.0.16 h1:0dKzYO6gTAvuLaAKQkC02eCPjMIi4NuAr/ibAwrGDCo=
|
github.com/pion/sdp/v3 v3.0.16 h1:0dKzYO6gTAvuLaAKQkC02eCPjMIi4NuAr/ibAwrGDCo=
|
||||||
github.com/pion/sdp/v3 v3.0.16/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo=
|
github.com/pion/sdp/v3 v3.0.16/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo=
|
||||||
github.com/pion/srtp/v3 v3.0.7 h1:QUElw0A/FUg3MP8/KNMZB3i0m8F9XeMnTum86F7S4bs=
|
github.com/pion/srtp/v3 v3.0.8 h1:RjRrjcIeQsilPzxvdaElN0CpuQZdMvcl9VZ5UY9suUM=
|
||||||
github.com/pion/srtp/v3 v3.0.7/go.mod h1:qvnHeqbhT7kDdB+OGB05KA/P067G3mm7XBfLaLiaNF0=
|
github.com/pion/srtp/v3 v3.0.8/go.mod h1:2Sq6YnDH7/UDCvkSoHSDNDeyBcFgWL0sAVycVbAsXFg=
|
||||||
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
||||||
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
||||||
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
github.com/pion/transport/v3 v3.0.8 h1:oI3myyYnTKUSTthu/NZZ8eu2I5sHbxbUNNFW62olaYc=
|
||||||
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
github.com/pion/transport/v3 v3.0.8/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ=
|
||||||
github.com/pion/turn/v4 v4.1.1 h1:9UnY2HB99tpDyz3cVVZguSxcqkJ1DsTSZ+8TGruh4fc=
|
github.com/pion/turn/v4 v4.1.1 h1:9UnY2HB99tpDyz3cVVZguSxcqkJ1DsTSZ+8TGruh4fc=
|
||||||
github.com/pion/turn/v4 v4.1.1/go.mod h1:2123tHk1O++vmjI5VSD0awT50NywDAq5A2NNNU4Jjs8=
|
github.com/pion/turn/v4 v4.1.1/go.mod h1:2123tHk1O++vmjI5VSD0awT50NywDAq5A2NNNU4Jjs8=
|
||||||
github.com/pion/webrtc/v4 v4.1.4 h1:/gK1ACGHXQmtyVVbJFQDxNoODg4eSRiFLB7t9r9pg8M=
|
github.com/pion/webrtc/v4 v4.1.6 h1:srHH2HwvCGwPba25EYJgUzgLqCQoXl1VCUnrGQMSzUw=
|
||||||
github.com/pion/webrtc/v4 v4.1.4/go.mod h1:Oab9npu1iZtQRMic3K3toYq5zFPvToe/QBw7dMI2ok4=
|
github.com/pion/webrtc/v4 v4.1.6/go.mod h1:wKecGRlkl3ox/As/MYghJL+b/cVXMEhoPMJWPuGQFhU=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
|
@ -157,10 +157,10 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h
|
||||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8=
|
||||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko=
|
||||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||||
github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE=
|
github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE=
|
||||||
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
|
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
|
|
@ -202,16 +202,16 @@ go.bug.st/serial v1.6.4 h1:7FmqNPgVp3pu2Jz5PoPtbZ9jJO5gnEnZIvnI1lzve8A=
|
||||||
go.bug.st/serial v1.6.4/go.mod h1:nofMJxTeNVny/m6+KaafC6vJGj3miwQZ6vW4BZUGJPI=
|
go.bug.st/serial v1.6.4/go.mod h1:nofMJxTeNVny/m6+KaafC6vJGj3miwQZ6vW4BZUGJPI=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||||
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
||||||
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|
@ -220,14 +220,14 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|
|
||||||
203
jsonrpc.go
203
jsonrpc.go
|
|
@ -946,10 +946,16 @@ func rpcSetUsbDeviceState(device string, enabled bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcGetAudioOutputEnabled() (bool, error) {
|
func rpcGetAudioOutputEnabled() (bool, error) {
|
||||||
return audioOutputEnabled.Load(), nil
|
ensureConfigLoaded()
|
||||||
|
return config.AudioOutputEnabled, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcSetAudioOutputEnabled(enabled bool) error {
|
func rpcSetAudioOutputEnabled(enabled bool) error {
|
||||||
|
ensureConfigLoaded()
|
||||||
|
config.AudioOutputEnabled = enabled
|
||||||
|
if err := SaveConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return SetAudioOutputEnabled(enabled)
|
return SetAudioOutputEnabled(enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -961,6 +967,17 @@ func rpcSetAudioInputEnabled(enabled bool) error {
|
||||||
return SetAudioInputEnabled(enabled)
|
return SetAudioInputEnabled(enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rpcGetAudioInputAutoEnable() (bool, error) {
|
||||||
|
ensureConfigLoaded()
|
||||||
|
return config.AudioInputAutoEnable, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func rpcSetAudioInputAutoEnable(enabled bool) error {
|
||||||
|
ensureConfigLoaded()
|
||||||
|
config.AudioInputAutoEnable = enabled
|
||||||
|
return SaveConfig()
|
||||||
|
}
|
||||||
|
|
||||||
func rpcSetCloudUrl(apiUrl string, appUrl string) error {
|
func rpcSetCloudUrl(apiUrl string, appUrl string) error {
|
||||||
currentCloudURL := config.CloudURL
|
currentCloudURL := config.CloudURL
|
||||||
config.CloudURL = apiUrl
|
config.CloudURL = apiUrl
|
||||||
|
|
@ -1199,95 +1216,97 @@ func rpcDoExecuteKeyboardMacro(ctx context.Context, macro []hidrpc.KeyboardMacro
|
||||||
}
|
}
|
||||||
|
|
||||||
var rpcHandlers = map[string]RPCHandler{
|
var rpcHandlers = map[string]RPCHandler{
|
||||||
"ping": {Func: rpcPing},
|
"ping": {Func: rpcPing},
|
||||||
"reboot": {Func: rpcReboot, Params: []string{"force"}},
|
"reboot": {Func: rpcReboot, Params: []string{"force"}},
|
||||||
"getDeviceID": {Func: rpcGetDeviceID},
|
"getDeviceID": {Func: rpcGetDeviceID},
|
||||||
"deregisterDevice": {Func: rpcDeregisterDevice},
|
"deregisterDevice": {Func: rpcDeregisterDevice},
|
||||||
"getCloudState": {Func: rpcGetCloudState},
|
"getCloudState": {Func: rpcGetCloudState},
|
||||||
"getNetworkState": {Func: rpcGetNetworkState},
|
"getNetworkState": {Func: rpcGetNetworkState},
|
||||||
"getNetworkSettings": {Func: rpcGetNetworkSettings},
|
"getNetworkSettings": {Func: rpcGetNetworkSettings},
|
||||||
"setNetworkSettings": {Func: rpcSetNetworkSettings, Params: []string{"settings"}},
|
"setNetworkSettings": {Func: rpcSetNetworkSettings, Params: []string{"settings"}},
|
||||||
"renewDHCPLease": {Func: rpcRenewDHCPLease},
|
"renewDHCPLease": {Func: rpcRenewDHCPLease},
|
||||||
"getKeyboardLedState": {Func: rpcGetKeyboardLedState},
|
"getKeyboardLedState": {Func: rpcGetKeyboardLedState},
|
||||||
"getKeyDownState": {Func: rpcGetKeysDownState},
|
"getKeyDownState": {Func: rpcGetKeysDownState},
|
||||||
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}},
|
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}},
|
||||||
"keypressReport": {Func: rpcKeypressReport, Params: []string{"key", "press"}},
|
"keypressReport": {Func: rpcKeypressReport, Params: []string{"key", "press"}},
|
||||||
"absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}},
|
"absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}},
|
||||||
"relMouseReport": {Func: rpcRelMouseReport, Params: []string{"dx", "dy", "buttons"}},
|
"relMouseReport": {Func: rpcRelMouseReport, Params: []string{"dx", "dy", "buttons"}},
|
||||||
"wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}},
|
"wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}},
|
||||||
"getVideoState": {Func: rpcGetVideoState},
|
"getVideoState": {Func: rpcGetVideoState},
|
||||||
"getUSBState": {Func: rpcGetUSBState},
|
"getUSBState": {Func: rpcGetUSBState},
|
||||||
"unmountImage": {Func: rpcUnmountImage},
|
"unmountImage": {Func: rpcUnmountImage},
|
||||||
"rpcMountBuiltInImage": {Func: rpcMountBuiltInImage, Params: []string{"filename"}},
|
"rpcMountBuiltInImage": {Func: rpcMountBuiltInImage, Params: []string{"filename"}},
|
||||||
"setJigglerState": {Func: rpcSetJigglerState, Params: []string{"enabled"}},
|
"setJigglerState": {Func: rpcSetJigglerState, Params: []string{"enabled"}},
|
||||||
"getJigglerState": {Func: rpcGetJigglerState},
|
"getJigglerState": {Func: rpcGetJigglerState},
|
||||||
"setJigglerConfig": {Func: rpcSetJigglerConfig, Params: []string{"jigglerConfig"}},
|
"setJigglerConfig": {Func: rpcSetJigglerConfig, Params: []string{"jigglerConfig"}},
|
||||||
"getJigglerConfig": {Func: rpcGetJigglerConfig},
|
"getJigglerConfig": {Func: rpcGetJigglerConfig},
|
||||||
"getTimezones": {Func: rpcGetTimezones},
|
"getTimezones": {Func: rpcGetTimezones},
|
||||||
"sendWOLMagicPacket": {Func: rpcSendWOLMagicPacket, Params: []string{"macAddress"}},
|
"sendWOLMagicPacket": {Func: rpcSendWOLMagicPacket, Params: []string{"macAddress"}},
|
||||||
"getStreamQualityFactor": {Func: rpcGetStreamQualityFactor},
|
"getStreamQualityFactor": {Func: rpcGetStreamQualityFactor},
|
||||||
"setStreamQualityFactor": {Func: rpcSetStreamQualityFactor, Params: []string{"factor"}},
|
"setStreamQualityFactor": {Func: rpcSetStreamQualityFactor, Params: []string{"factor"}},
|
||||||
"getAutoUpdateState": {Func: rpcGetAutoUpdateState},
|
"getAutoUpdateState": {Func: rpcGetAutoUpdateState},
|
||||||
"setAutoUpdateState": {Func: rpcSetAutoUpdateState, Params: []string{"enabled"}},
|
"setAutoUpdateState": {Func: rpcSetAutoUpdateState, Params: []string{"enabled"}},
|
||||||
"getEDID": {Func: rpcGetEDID},
|
"getEDID": {Func: rpcGetEDID},
|
||||||
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
|
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
|
||||||
"getVideoLogStatus": {Func: rpcGetVideoLogStatus},
|
"getVideoLogStatus": {Func: rpcGetVideoLogStatus},
|
||||||
"getVideoSleepMode": {Func: rpcGetVideoSleepMode},
|
"getVideoSleepMode": {Func: rpcGetVideoSleepMode},
|
||||||
"setVideoSleepMode": {Func: rpcSetVideoSleepMode, Params: []string{"duration"}},
|
"setVideoSleepMode": {Func: rpcSetVideoSleepMode, Params: []string{"duration"}},
|
||||||
"getDevChannelState": {Func: rpcGetDevChannelState},
|
"getDevChannelState": {Func: rpcGetDevChannelState},
|
||||||
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
|
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
|
||||||
"getLocalVersion": {Func: rpcGetLocalVersion},
|
"getLocalVersion": {Func: rpcGetLocalVersion},
|
||||||
"getUpdateStatus": {Func: rpcGetUpdateStatus},
|
"getUpdateStatus": {Func: rpcGetUpdateStatus},
|
||||||
"tryUpdate": {Func: rpcTryUpdate},
|
"tryUpdate": {Func: rpcTryUpdate},
|
||||||
"getDevModeState": {Func: rpcGetDevModeState},
|
"getDevModeState": {Func: rpcGetDevModeState},
|
||||||
"setDevModeState": {Func: rpcSetDevModeState, Params: []string{"enabled"}},
|
"setDevModeState": {Func: rpcSetDevModeState, Params: []string{"enabled"}},
|
||||||
"getSSHKeyState": {Func: rpcGetSSHKeyState},
|
"getSSHKeyState": {Func: rpcGetSSHKeyState},
|
||||||
"setSSHKeyState": {Func: rpcSetSSHKeyState, Params: []string{"sshKey"}},
|
"setSSHKeyState": {Func: rpcSetSSHKeyState, Params: []string{"sshKey"}},
|
||||||
"getTLSState": {Func: rpcGetTLSState},
|
"getTLSState": {Func: rpcGetTLSState},
|
||||||
"setTLSState": {Func: rpcSetTLSState, Params: []string{"state"}},
|
"setTLSState": {Func: rpcSetTLSState, Params: []string{"state"}},
|
||||||
"setMassStorageMode": {Func: rpcSetMassStorageMode, Params: []string{"mode"}},
|
"setMassStorageMode": {Func: rpcSetMassStorageMode, Params: []string{"mode"}},
|
||||||
"getMassStorageMode": {Func: rpcGetMassStorageMode},
|
"getMassStorageMode": {Func: rpcGetMassStorageMode},
|
||||||
"isUpdatePending": {Func: rpcIsUpdatePending},
|
"isUpdatePending": {Func: rpcIsUpdatePending},
|
||||||
"getUsbEmulationState": {Func: rpcGetUsbEmulationState},
|
"getUsbEmulationState": {Func: rpcGetUsbEmulationState},
|
||||||
"setUsbEmulationState": {Func: rpcSetUsbEmulationState, Params: []string{"enabled"}},
|
"setUsbEmulationState": {Func: rpcSetUsbEmulationState, Params: []string{"enabled"}},
|
||||||
"getUsbConfig": {Func: rpcGetUsbConfig},
|
"getUsbConfig": {Func: rpcGetUsbConfig},
|
||||||
"setUsbConfig": {Func: rpcSetUsbConfig, Params: []string{"usbConfig"}},
|
"setUsbConfig": {Func: rpcSetUsbConfig, Params: []string{"usbConfig"}},
|
||||||
"checkMountUrl": {Func: rpcCheckMountUrl, Params: []string{"url"}},
|
"checkMountUrl": {Func: rpcCheckMountUrl, Params: []string{"url"}},
|
||||||
"getVirtualMediaState": {Func: rpcGetVirtualMediaState},
|
"getVirtualMediaState": {Func: rpcGetVirtualMediaState},
|
||||||
"getStorageSpace": {Func: rpcGetStorageSpace},
|
"getStorageSpace": {Func: rpcGetStorageSpace},
|
||||||
"mountWithHTTP": {Func: rpcMountWithHTTP, Params: []string{"url", "mode"}},
|
"mountWithHTTP": {Func: rpcMountWithHTTP, Params: []string{"url", "mode"}},
|
||||||
"mountWithStorage": {Func: rpcMountWithStorage, Params: []string{"filename", "mode"}},
|
"mountWithStorage": {Func: rpcMountWithStorage, Params: []string{"filename", "mode"}},
|
||||||
"listStorageFiles": {Func: rpcListStorageFiles},
|
"listStorageFiles": {Func: rpcListStorageFiles},
|
||||||
"deleteStorageFile": {Func: rpcDeleteStorageFile, Params: []string{"filename"}},
|
"deleteStorageFile": {Func: rpcDeleteStorageFile, Params: []string{"filename"}},
|
||||||
"startStorageFileUpload": {Func: rpcStartStorageFileUpload, Params: []string{"filename", "size"}},
|
"startStorageFileUpload": {Func: rpcStartStorageFileUpload, Params: []string{"filename", "size"}},
|
||||||
"getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices},
|
"getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices},
|
||||||
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}},
|
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}},
|
||||||
"resetConfig": {Func: rpcResetConfig},
|
"resetConfig": {Func: rpcResetConfig},
|
||||||
"setDisplayRotation": {Func: rpcSetDisplayRotation, Params: []string{"params"}},
|
"setDisplayRotation": {Func: rpcSetDisplayRotation, Params: []string{"params"}},
|
||||||
"getDisplayRotation": {Func: rpcGetDisplayRotation},
|
"getDisplayRotation": {Func: rpcGetDisplayRotation},
|
||||||
"setBacklightSettings": {Func: rpcSetBacklightSettings, Params: []string{"params"}},
|
"setBacklightSettings": {Func: rpcSetBacklightSettings, Params: []string{"params"}},
|
||||||
"getBacklightSettings": {Func: rpcGetBacklightSettings},
|
"getBacklightSettings": {Func: rpcGetBacklightSettings},
|
||||||
"getDCPowerState": {Func: rpcGetDCPowerState},
|
"getDCPowerState": {Func: rpcGetDCPowerState},
|
||||||
"setDCPowerState": {Func: rpcSetDCPowerState, Params: []string{"enabled"}},
|
"setDCPowerState": {Func: rpcSetDCPowerState, Params: []string{"enabled"}},
|
||||||
"setDCRestoreState": {Func: rpcSetDCRestoreState, Params: []string{"state"}},
|
"setDCRestoreState": {Func: rpcSetDCRestoreState, Params: []string{"state"}},
|
||||||
"getActiveExtension": {Func: rpcGetActiveExtension},
|
"getActiveExtension": {Func: rpcGetActiveExtension},
|
||||||
"setActiveExtension": {Func: rpcSetActiveExtension, Params: []string{"extensionId"}},
|
"setActiveExtension": {Func: rpcSetActiveExtension, Params: []string{"extensionId"}},
|
||||||
"getATXState": {Func: rpcGetATXState},
|
"getATXState": {Func: rpcGetATXState},
|
||||||
"setATXPowerAction": {Func: rpcSetATXPowerAction, Params: []string{"action"}},
|
"setATXPowerAction": {Func: rpcSetATXPowerAction, Params: []string{"action"}},
|
||||||
"getSerialSettings": {Func: rpcGetSerialSettings},
|
"getSerialSettings": {Func: rpcGetSerialSettings},
|
||||||
"setSerialSettings": {Func: rpcSetSerialSettings, Params: []string{"settings"}},
|
"setSerialSettings": {Func: rpcSetSerialSettings, Params: []string{"settings"}},
|
||||||
"getUsbDevices": {Func: rpcGetUsbDevices},
|
"getUsbDevices": {Func: rpcGetUsbDevices},
|
||||||
"setUsbDevices": {Func: rpcSetUsbDevices, Params: []string{"devices"}},
|
"setUsbDevices": {Func: rpcSetUsbDevices, Params: []string{"devices"}},
|
||||||
"setUsbDeviceState": {Func: rpcSetUsbDeviceState, Params: []string{"device", "enabled"}},
|
"setUsbDeviceState": {Func: rpcSetUsbDeviceState, Params: []string{"device", "enabled"}},
|
||||||
"getAudioOutputEnabled": {Func: rpcGetAudioOutputEnabled},
|
"getAudioOutputEnabled": {Func: rpcGetAudioOutputEnabled},
|
||||||
"setAudioOutputEnabled": {Func: rpcSetAudioOutputEnabled, Params: []string{"enabled"}},
|
"setAudioOutputEnabled": {Func: rpcSetAudioOutputEnabled, Params: []string{"enabled"}},
|
||||||
"getAudioInputEnabled": {Func: rpcGetAudioInputEnabled},
|
"getAudioInputEnabled": {Func: rpcGetAudioInputEnabled},
|
||||||
"setAudioInputEnabled": {Func: rpcSetAudioInputEnabled, Params: []string{"enabled"}},
|
"setAudioInputEnabled": {Func: rpcSetAudioInputEnabled, Params: []string{"enabled"}},
|
||||||
"setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}},
|
"getAudioInputAutoEnable": {Func: rpcGetAudioInputAutoEnable},
|
||||||
"getKeyboardLayout": {Func: rpcGetKeyboardLayout},
|
"setAudioInputAutoEnable": {Func: rpcSetAudioInputAutoEnable, Params: []string{"enabled"}},
|
||||||
"setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"layout"}},
|
"setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}},
|
||||||
"getKeyboardMacros": {Func: getKeyboardMacros},
|
"getKeyboardLayout": {Func: rpcGetKeyboardLayout},
|
||||||
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
|
"setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"layout"}},
|
||||||
"getLocalLoopbackOnly": {Func: rpcGetLocalLoopbackOnly},
|
"getKeyboardMacros": {Func: getKeyboardMacros},
|
||||||
"setLocalLoopbackOnly": {Func: rpcSetLocalLoopbackOnly, Params: []string{"enabled"}},
|
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
|
||||||
|
"getLocalLoopbackOnly": {Func: rpcGetLocalLoopbackOnly},
|
||||||
|
"setLocalLoopbackOnly": {Func: rpcSetLocalLoopbackOnly, Params: []string{"enabled"}},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@
|
||||||
"audio_input_failed_disable": "Kunne ikke deaktivere lydindgang: {error}",
|
"audio_input_failed_disable": "Kunne ikke deaktivere lydindgang: {error}",
|
||||||
"audio_input_failed_enable": "Kunne ikke aktivere lydindgang: {error}",
|
"audio_input_failed_enable": "Kunne ikke aktivere lydindgang: {error}",
|
||||||
"audio_input_title": "Lydindgang (Mikrofon)",
|
"audio_input_title": "Lydindgang (Mikrofon)",
|
||||||
|
"audio_input_auto_enable_disabled": "Automatisk aktivering af mikrofon deaktiveret",
|
||||||
|
"audio_input_auto_enable_enabled": "Automatisk aktivering af mikrofon aktiveret",
|
||||||
"audio_output_description": "Aktiver lyd fra mål til højttalere",
|
"audio_output_description": "Aktiver lyd fra mål til højttalere",
|
||||||
"audio_output_disabled": "Lydudgang deaktiveret",
|
"audio_output_disabled": "Lydudgang deaktiveret",
|
||||||
"audio_output_enabled": "Lydudgang aktiveret",
|
"audio_output_enabled": "Lydudgang aktiveret",
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@
|
||||||
"audio_input_failed_disable": "Fehler beim Deaktivieren des Audioeingangs: {error}",
|
"audio_input_failed_disable": "Fehler beim Deaktivieren des Audioeingangs: {error}",
|
||||||
"audio_input_failed_enable": "Fehler beim Aktivieren des Audioeingangs: {error}",
|
"audio_input_failed_enable": "Fehler beim Aktivieren des Audioeingangs: {error}",
|
||||||
"audio_input_title": "Audioeingang (Mikrofon)",
|
"audio_input_title": "Audioeingang (Mikrofon)",
|
||||||
|
"audio_input_auto_enable_disabled": "Automatische Mikrofonaktivierung deaktiviert",
|
||||||
|
"audio_input_auto_enable_enabled": "Automatische Mikrofonaktivierung aktiviert",
|
||||||
"audio_output_description": "Audio vom Ziel zu Lautsprechern aktivieren",
|
"audio_output_description": "Audio vom Ziel zu Lautsprechern aktivieren",
|
||||||
"audio_output_disabled": "Audioausgang deaktiviert",
|
"audio_output_disabled": "Audioausgang deaktiviert",
|
||||||
"audio_output_enabled": "Audioausgang aktiviert",
|
"audio_output_enabled": "Audioausgang aktiviert",
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@
|
||||||
"audio_input_failed_disable": "Failed to disable audio input: {error}",
|
"audio_input_failed_disable": "Failed to disable audio input: {error}",
|
||||||
"audio_input_failed_enable": "Failed to enable audio input: {error}",
|
"audio_input_failed_enable": "Failed to enable audio input: {error}",
|
||||||
"audio_input_title": "Audio Input (Microphone)",
|
"audio_input_title": "Audio Input (Microphone)",
|
||||||
|
"audio_input_auto_enable_disabled": "Auto-enable microphone disabled",
|
||||||
|
"audio_input_auto_enable_enabled": "Auto-enable microphone enabled",
|
||||||
"audio_output_description": "Enable audio from target to speakers",
|
"audio_output_description": "Enable audio from target to speakers",
|
||||||
"audio_output_disabled": "Audio output disabled",
|
"audio_output_disabled": "Audio output disabled",
|
||||||
"audio_output_enabled": "Audio output enabled",
|
"audio_output_enabled": "Audio output enabled",
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@
|
||||||
"audio_input_failed_disable": "Error al desactivar la entrada de audio: {error}",
|
"audio_input_failed_disable": "Error al desactivar la entrada de audio: {error}",
|
||||||
"audio_input_failed_enable": "Error al activar la entrada de audio: {error}",
|
"audio_input_failed_enable": "Error al activar la entrada de audio: {error}",
|
||||||
"audio_input_title": "Entrada de audio (Micrófono)",
|
"audio_input_title": "Entrada de audio (Micrófono)",
|
||||||
|
"audio_input_auto_enable_disabled": "Habilitación automática de micrófono desactivada",
|
||||||
|
"audio_input_auto_enable_enabled": "Habilitación automática de micrófono activada",
|
||||||
"audio_output_description": "Habilitar audio del objetivo a los altavoces",
|
"audio_output_description": "Habilitar audio del objetivo a los altavoces",
|
||||||
"audio_output_disabled": "Salida de audio desactivada",
|
"audio_output_disabled": "Salida de audio desactivada",
|
||||||
"audio_output_enabled": "Salida de audio activada",
|
"audio_output_enabled": "Salida de audio activada",
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@
|
||||||
"audio_input_failed_disable": "Échec de la désactivation de l'entrée audio : {error}",
|
"audio_input_failed_disable": "Échec de la désactivation de l'entrée audio : {error}",
|
||||||
"audio_input_failed_enable": "Échec de l'activation de l'entrée audio : {error}",
|
"audio_input_failed_enable": "Échec de l'activation de l'entrée audio : {error}",
|
||||||
"audio_input_title": "Entrée audio (Microphone)",
|
"audio_input_title": "Entrée audio (Microphone)",
|
||||||
|
"audio_input_auto_enable_disabled": "Activation automatique du microphone désactivée",
|
||||||
|
"audio_input_auto_enable_enabled": "Activation automatique du microphone activée",
|
||||||
"audio_output_description": "Activer l'audio de la cible vers les haut-parleurs",
|
"audio_output_description": "Activer l'audio de la cible vers les haut-parleurs",
|
||||||
"audio_output_disabled": "Sortie audio désactivée",
|
"audio_output_disabled": "Sortie audio désactivée",
|
||||||
"audio_output_enabled": "Sortie audio activée",
|
"audio_output_enabled": "Sortie audio activée",
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@
|
||||||
"audio_input_failed_disable": "Impossibile disabilitare l'ingresso audio: {error}",
|
"audio_input_failed_disable": "Impossibile disabilitare l'ingresso audio: {error}",
|
||||||
"audio_input_failed_enable": "Impossibile abilitare l'ingresso audio: {error}",
|
"audio_input_failed_enable": "Impossibile abilitare l'ingresso audio: {error}",
|
||||||
"audio_input_title": "Ingresso audio (Microfono)",
|
"audio_input_title": "Ingresso audio (Microfono)",
|
||||||
|
"audio_input_auto_enable_disabled": "Abilitazione automatica microfono disabilitata",
|
||||||
|
"audio_input_auto_enable_enabled": "Abilitazione automatica microfono abilitata",
|
||||||
"audio_output_description": "Abilita l'audio dal target agli altoparlanti",
|
"audio_output_description": "Abilita l'audio dal target agli altoparlanti",
|
||||||
"audio_output_disabled": "Uscita audio disabilitata",
|
"audio_output_disabled": "Uscita audio disabilitata",
|
||||||
"audio_output_enabled": "Uscita audio abilitata",
|
"audio_output_enabled": "Uscita audio abilitata",
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@
|
||||||
"audio_input_failed_disable": "Kunne ikke deaktivere lydinngang: {error}",
|
"audio_input_failed_disable": "Kunne ikke deaktivere lydinngang: {error}",
|
||||||
"audio_input_failed_enable": "Kunne ikke aktivere lydinngang: {error}",
|
"audio_input_failed_enable": "Kunne ikke aktivere lydinngang: {error}",
|
||||||
"audio_input_title": "Lydinngang (Mikrofon)",
|
"audio_input_title": "Lydinngang (Mikrofon)",
|
||||||
|
"audio_input_auto_enable_disabled": "Automatisk aktivering av mikrofon deaktivert",
|
||||||
|
"audio_input_auto_enable_enabled": "Automatisk aktivering av mikrofon aktivert",
|
||||||
"audio_output_description": "Aktiver lyd fra mål til høyttalere",
|
"audio_output_description": "Aktiver lyd fra mål til høyttalere",
|
||||||
"audio_output_disabled": "Lydutgang deaktivert",
|
"audio_output_disabled": "Lydutgang deaktivert",
|
||||||
"audio_output_enabled": "Lydutgang aktivert",
|
"audio_output_enabled": "Lydutgang aktivert",
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@
|
||||||
"audio_input_failed_disable": "Det gick inte att inaktivera ljudingången: {error}",
|
"audio_input_failed_disable": "Det gick inte att inaktivera ljudingången: {error}",
|
||||||
"audio_input_failed_enable": "Det gick inte att aktivera ljudingången: {error}",
|
"audio_input_failed_enable": "Det gick inte att aktivera ljudingången: {error}",
|
||||||
"audio_input_title": "Ljudingång (Mikrofon)",
|
"audio_input_title": "Ljudingång (Mikrofon)",
|
||||||
|
"audio_input_auto_enable_disabled": "Automatisk aktivering av mikrofon inaktiverad",
|
||||||
|
"audio_input_auto_enable_enabled": "Automatisk aktivering av mikrofon aktiverad",
|
||||||
"audio_output_description": "Aktivera ljud från mål till högtalare",
|
"audio_output_description": "Aktivera ljud från mål till högtalare",
|
||||||
"audio_output_disabled": "Ljudutgång inaktiverad",
|
"audio_output_disabled": "Ljudutgång inaktiverad",
|
||||||
"audio_output_enabled": "Ljudutgång aktiverad",
|
"audio_output_enabled": "Ljudutgång aktiverad",
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@
|
||||||
"audio_input_failed_disable": "禁用音频输入失败:{error}",
|
"audio_input_failed_disable": "禁用音频输入失败:{error}",
|
||||||
"audio_input_failed_enable": "启用音频输入失败:{error}",
|
"audio_input_failed_enable": "启用音频输入失败:{error}",
|
||||||
"audio_input_title": "音频输入(麦克风)",
|
"audio_input_title": "音频输入(麦克风)",
|
||||||
|
"audio_input_auto_enable_disabled": "自动启用麦克风已禁用",
|
||||||
|
"audio_input_auto_enable_enabled": "自动启用麦克风已启用",
|
||||||
"audio_output_description": "启用从目标设备到扬声器的音频",
|
"audio_output_description": "启用从目标设备到扬声器的音频",
|
||||||
"audio_output_disabled": "音频输出已禁用",
|
"audio_output_disabled": "音频输出已禁用",
|
||||||
"audio_output_enabled": "音频输出已启用",
|
"audio_output_enabled": "音频输出已启用",
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
"react-hook-form": "^7.65.0",
|
"react-hook-form": "^7.65.0",
|
||||||
"react-hot-toast": "^2.6.0",
|
"react-hot-toast": "^2.6.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-router": "^7.9.4",
|
"react-router": "^7.9.5",
|
||||||
"react-simple-keyboard": "^3.8.131",
|
"react-simple-keyboard": "^3.8.131",
|
||||||
"react-use-websocket": "^4.13.0",
|
"react-use-websocket": "^4.13.0",
|
||||||
"react-xtermjs": "^1.0.10",
|
"react-xtermjs": "^1.0.10",
|
||||||
|
|
@ -42,9 +42,9 @@
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.4.0",
|
"@eslint/compat": "^1.4.1",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.38.0",
|
"@eslint/js": "^9.39.0",
|
||||||
"@inlang/cli": "^3.0.12",
|
"@inlang/cli": "^3.0.12",
|
||||||
"@inlang/paraglide-js": "^2.4.0",
|
"@inlang/paraglide-js": "^2.4.0",
|
||||||
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
||||||
|
|
@ -799,13 +799,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/compat": {
|
"node_modules/@eslint/compat": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.4.1.tgz",
|
||||||
"integrity": "sha512-DEzm5dKeDBPm3r08Ixli/0cmxr8LkRdwxMRUIJBlSCpAwSrvFEJpVBzV+66JhDxiaqKxnRzCXhtiMiczF7Hglg==",
|
"integrity": "sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/core": "^0.16.0"
|
"@eslint/core": "^0.17.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
|
@ -819,6 +819,19 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@eslint/compat/node_modules/@eslint/core": {
|
||||||
|
"version": "0.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
|
||||||
|
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/json-schema": "^7.0.15"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@eslint/config-array": {
|
"node_modules/@eslint/config-array": {
|
||||||
"version": "0.21.1",
|
"version": "0.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
|
||||||
|
|
@ -893,9 +906,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.38.0",
|
"version": "9.39.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.0.tgz",
|
||||||
"integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==",
|
"integrity": "sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
|
@ -2198,6 +2212,66 @@
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"dev": true,
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/wasi-threads": "1.1.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"dev": true,
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"dev": true,
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"dev": true,
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/core": "^1.5.0",
|
||||||
|
"@emnapi/runtime": "^1.5.0",
|
||||||
|
"@tybys/wasm-util": "^0.10.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
|
||||||
|
"version": "0.10.1",
|
||||||
|
"dev": true,
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"dev": true,
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "0BSD",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||||
"version": "4.1.16",
|
"version": "4.1.16",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.16.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.16.tgz",
|
||||||
|
|
@ -4268,6 +4342,18 @@
|
||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint/node_modules/@eslint/js": {
|
||||||
|
"version": "9.38.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz",
|
||||||
|
"integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://eslint.org/donate"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint/node_modules/eslint-visitor-keys": {
|
"node_modules/eslint/node_modules/eslint-visitor-keys": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
||||||
|
|
@ -6519,9 +6605,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "7.9.4",
|
"version": "7.9.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.5.tgz",
|
||||||
"integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==",
|
"integrity": "sha512-JmxqrnBZ6E9hWmf02jzNn9Jm3UqyeimyiwzD69NjxGySG6lIz/1LVPsoTCwN7NBX2XjCEa1LIX5EMz1j2b6u6A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": "^1.0.1",
|
"cookie": "^1.0.1",
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
"react-hook-form": "^7.65.0",
|
"react-hook-form": "^7.65.0",
|
||||||
"react-hot-toast": "^2.6.0",
|
"react-hot-toast": "^2.6.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-router": "^7.9.4",
|
"react-router": "^7.9.5",
|
||||||
"react-simple-keyboard": "^3.8.131",
|
"react-simple-keyboard": "^3.8.131",
|
||||||
"react-use-websocket": "^4.13.0",
|
"react-use-websocket": "^4.13.0",
|
||||||
"react-xtermjs": "^1.0.10",
|
"react-xtermjs": "^1.0.10",
|
||||||
|
|
@ -61,9 +61,9 @@
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.4.0",
|
"@eslint/compat": "^1.4.1",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.38.0",
|
"@eslint/js": "^9.39.0",
|
||||||
"@inlang/cli": "^3.0.12",
|
"@inlang/cli": "^3.0.12",
|
||||||
"@inlang/paraglide-js": "^2.4.0",
|
"@inlang/paraglide-js": "^2.4.0",
|
||||||
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ export default function AudioPopover() {
|
||||||
const [audioOutputEnabled, setAudioOutputEnabled] = useState<boolean>(true);
|
const [audioOutputEnabled, setAudioOutputEnabled] = useState<boolean>(true);
|
||||||
const [usbAudioEnabled, setUsbAudioEnabled] = useState<boolean>(false);
|
const [usbAudioEnabled, setUsbAudioEnabled] = useState<boolean>(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [micLoading, setMicLoading] = useState(false);
|
||||||
const isHttps = isSecureContext();
|
const isHttps = isSecureContext();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -54,6 +55,21 @@ export default function AudioPopover() {
|
||||||
});
|
});
|
||||||
}, [send]);
|
}, [send]);
|
||||||
|
|
||||||
|
const handleMicrophoneToggle = useCallback((enabled: boolean) => {
|
||||||
|
setMicLoading(true);
|
||||||
|
send("setAudioInputEnabled", { enabled }, (resp: JsonRpcResponse) => {
|
||||||
|
setMicLoading(false);
|
||||||
|
if ("error" in resp) {
|
||||||
|
const errorMsg = enabled
|
||||||
|
? m.audio_input_failed_enable({ error: String(resp.error.data || m.unknown_error()) })
|
||||||
|
: m.audio_input_failed_disable({ error: String(resp.error.data || m.unknown_error()) });
|
||||||
|
notifications.error(errorMsg);
|
||||||
|
} else {
|
||||||
|
setMicrophoneEnabled(enabled);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [send, setMicrophoneEnabled]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridCard>
|
<GridCard>
|
||||||
<div className="space-y-4 p-4 py-3">
|
<div className="space-y-4 p-4 py-3">
|
||||||
|
|
@ -80,6 +96,7 @@ export default function AudioPopover() {
|
||||||
<div className="h-px w-full bg-slate-800/10 dark:bg-slate-300/20" />
|
<div className="h-px w-full bg-slate-800/10 dark:bg-slate-300/20" />
|
||||||
|
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
|
loading={micLoading}
|
||||||
title={m.audio_microphone_title()}
|
title={m.audio_microphone_title()}
|
||||||
description={m.audio_microphone_description()}
|
description={m.audio_microphone_description()}
|
||||||
badge={!isHttps ? m.audio_https_only() : undefined}
|
badge={!isHttps ? m.audio_https_only() : undefined}
|
||||||
|
|
@ -89,7 +106,7 @@ export default function AudioPopover() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={microphoneEnabled}
|
checked={microphoneEnabled}
|
||||||
disabled={!isHttps}
|
disabled={!isHttps}
|
||||||
onChange={(e) => setMicrophoneEnabled(e.target.checked)}
|
onChange={(e) => handleMicrophoneToggle(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -382,6 +382,8 @@ export interface SettingsState {
|
||||||
setMicrophoneEnabled: (enabled: boolean) => void;
|
setMicrophoneEnabled: (enabled: boolean) => void;
|
||||||
audioInputAutoEnable: boolean;
|
audioInputAutoEnable: boolean;
|
||||||
setAudioInputAutoEnable: (enabled: boolean) => void;
|
setAudioInputAutoEnable: (enabled: boolean) => void;
|
||||||
|
|
||||||
|
resetMicrophoneState: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSettingsStore = create(
|
export const useSettingsStore = create(
|
||||||
|
|
@ -430,13 +432,14 @@ export const useSettingsStore = create(
|
||||||
videoContrast: 1.0,
|
videoContrast: 1.0,
|
||||||
setVideoContrast: (value: number) => set({ videoContrast: value }),
|
setVideoContrast: (value: number) => set({ videoContrast: value }),
|
||||||
|
|
||||||
// Audio settings with defaults
|
|
||||||
audioOutputEnabled: true,
|
audioOutputEnabled: true,
|
||||||
setAudioOutputEnabled: (enabled: boolean) => set({ audioOutputEnabled: enabled }),
|
setAudioOutputEnabled: (enabled: boolean) => set({ audioOutputEnabled: enabled }),
|
||||||
microphoneEnabled: false,
|
microphoneEnabled: false,
|
||||||
setMicrophoneEnabled: (enabled: boolean) => set({ microphoneEnabled: enabled }),
|
setMicrophoneEnabled: (enabled: boolean) => set({ microphoneEnabled: enabled }),
|
||||||
audioInputAutoEnable: false,
|
audioInputAutoEnable: false,
|
||||||
setAudioInputAutoEnable: (enabled: boolean) => set({ audioInputAutoEnable: enabled }),
|
setAudioInputAutoEnable: (enabled: boolean) => set({ audioInputAutoEnable: enabled }),
|
||||||
|
|
||||||
|
resetMicrophoneState: () => set({ microphoneEnabled: false }),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "settings",
|
name: "settings",
|
||||||
|
|
|
||||||
|
|
@ -16,20 +16,15 @@ export default function SettingsAudioRoute() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
send("getAudioOutputEnabled", {}, (resp: JsonRpcResponse) => {
|
send("getAudioOutputEnabled", {}, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
settings.setAudioOutputEnabled(resp.result as boolean);
|
settings.setAudioOutputEnabled(resp.result as boolean);
|
||||||
});
|
});
|
||||||
|
|
||||||
send("getAudioInputEnabled", {}, (resp: JsonRpcResponse) => {
|
send("getAudioInputAutoEnable", {}, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
settings.setAudioInputAutoEnable(resp.result as boolean);
|
settings.setAudioInputAutoEnable(resp.result as boolean);
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [send, settings]);
|
||||||
}, [send]);
|
|
||||||
|
|
||||||
const handleAudioOutputEnabledChange = (enabled: boolean) => {
|
const handleAudioOutputEnabledChange = (enabled: boolean) => {
|
||||||
send("setAudioOutputEnabled", { enabled }, (resp: JsonRpcResponse) => {
|
send("setAudioOutputEnabled", { enabled }, (resp: JsonRpcResponse) => {
|
||||||
|
|
@ -47,16 +42,15 @@ export default function SettingsAudioRoute() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAudioInputAutoEnableChange = (enabled: boolean) => {
|
const handleAudioInputAutoEnableChange = (enabled: boolean) => {
|
||||||
send("setAudioInputEnabled", { enabled }, (resp: JsonRpcResponse) => {
|
send("setAudioInputAutoEnable", { enabled }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
const errorMsg = enabled
|
notifications.error(String(resp.error.data || m.unknown_error()));
|
||||||
? m.audio_input_failed_enable({ error: String(resp.error.data || m.unknown_error()) })
|
|
||||||
: m.audio_input_failed_disable({ error: String(resp.error.data || m.unknown_error()) });
|
|
||||||
notifications.error(errorMsg);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
settings.setAudioInputAutoEnable(enabled);
|
settings.setAudioInputAutoEnable(enabled);
|
||||||
const successMsg = enabled ? m.audio_input_enabled() : m.audio_input_disabled();
|
const successMsg = enabled
|
||||||
|
? m.audio_input_auto_enable_enabled()
|
||||||
|
: m.audio_input_auto_enable_disabled();
|
||||||
notifications.success(successMsg);
|
notifications.success(successMsg);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -538,11 +538,6 @@ export default function KvmIdRoute() {
|
||||||
const audioTrans = pc.addTransceiver("audio", { direction: "sendrecv" });
|
const audioTrans = pc.addTransceiver("audio", { direction: "sendrecv" });
|
||||||
setAudioTransceiver(audioTrans);
|
setAudioTransceiver(audioTrans);
|
||||||
|
|
||||||
// Enable microphone if auto-enable is on (only works over HTTPS or localhost)
|
|
||||||
if (audioInputAutoEnable && isSecureContext()) {
|
|
||||||
setMicrophoneEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const rpcDataChannel = pc.createDataChannel("rpc");
|
const rpcDataChannel = pc.createDataChannel("rpc");
|
||||||
rpcDataChannel.onclose = () => console.log("rpcDataChannel has closed");
|
rpcDataChannel.onclose = () => console.log("rpcDataChannel has closed");
|
||||||
rpcDataChannel.onerror = (ev: Event) => console.error(`Error on DataChannel '${rpcDataChannel.label}': ${ev}`);
|
rpcDataChannel.onerror = (ev: Event) => console.error(`Error on DataChannel '${rpcDataChannel.label}': ${ev}`);
|
||||||
|
|
@ -606,47 +601,39 @@ export default function KvmIdRoute() {
|
||||||
}
|
}
|
||||||
}, [peerConnectionState, cleanupAndStopReconnecting]);
|
}, [peerConnectionState, cleanupAndStopReconnecting]);
|
||||||
|
|
||||||
// Handle dynamic microphone enable/disable
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!audioTransceiver || !peerConnection) return;
|
if (!audioTransceiver || !peerConnection) return;
|
||||||
|
|
||||||
if (microphoneEnabled) {
|
if (microphoneEnabled) {
|
||||||
// Request microphone access
|
navigator.mediaDevices?.getUserMedia({
|
||||||
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
audio: {
|
||||||
navigator.mediaDevices.getUserMedia({
|
echoCancellation: true,
|
||||||
audio: {
|
noiseSuppression: true,
|
||||||
echoCancellation: true,
|
autoGainControl: true,
|
||||||
noiseSuppression: true,
|
channelCount: 2,
|
||||||
autoGainControl: true,
|
}
|
||||||
channelCount: 2,
|
}).then((stream) => {
|
||||||
}
|
const audioTrack = stream.getAudioTracks()[0];
|
||||||
}).then((stream) => {
|
if (audioTrack && audioTransceiver.sender) {
|
||||||
const audioTrack = stream.getAudioTracks()[0];
|
audioTransceiver.sender.replaceTrack(audioTrack);
|
||||||
if (audioTrack && audioTransceiver.sender) {
|
}
|
||||||
audioTransceiver.sender.replaceTrack(audioTrack);
|
}).catch(() => {
|
||||||
console.log("Microphone enabled");
|
setMicrophoneEnabled(false);
|
||||||
}
|
});
|
||||||
}).catch((err) => {
|
|
||||||
console.warn("Microphone access denied or unavailable:", err.message);
|
|
||||||
setMicrophoneEnabled(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Disable microphone by removing the track
|
|
||||||
if (audioTransceiver.sender.track) {
|
if (audioTransceiver.sender.track) {
|
||||||
audioTransceiver.sender.track.stop();
|
audioTransceiver.sender.track.stop();
|
||||||
audioTransceiver.sender.replaceTrack(null);
|
audioTransceiver.sender.replaceTrack(null);
|
||||||
console.log("Microphone disabled");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [microphoneEnabled, audioTransceiver, peerConnection, setMicrophoneEnabled]);
|
}, [microphoneEnabled, audioTransceiver, peerConnection]);
|
||||||
|
|
||||||
// Auto-enable microphone when setting is loaded from backend
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (audioInputAutoEnable && audioTransceiver && peerConnection && !microphoneEnabled && isSecureContext()) {
|
if (!audioTransceiver || !peerConnection || !audioInputAutoEnable || microphoneEnabled) return;
|
||||||
|
if (isSecureContext()) {
|
||||||
setMicrophoneEnabled(true);
|
setMicrophoneEnabled(true);
|
||||||
}
|
}
|
||||||
}, [audioInputAutoEnable, audioTransceiver, peerConnection, microphoneEnabled, setMicrophoneEnabled]);
|
}, [audioInputAutoEnable, audioTransceiver, peerConnection, microphoneEnabled]);
|
||||||
|
|
||||||
// Cleanup effect
|
// Cleanup effect
|
||||||
const { clearInboundRtpStats, clearCandidatePairStats } = useRTCStore();
|
const { clearInboundRtpStats, clearCandidatePairStats } = useRTCStore();
|
||||||
|
|
@ -806,15 +793,6 @@ export default function KvmIdRoute() {
|
||||||
|
|
||||||
const { send } = useJsonRpc(onJsonRpcRequest);
|
const { send } = useJsonRpc(onJsonRpcRequest);
|
||||||
|
|
||||||
// Load audio input auto-enable setting from backend on mount
|
|
||||||
useEffect(() => {
|
|
||||||
send("getAudioInputEnabled", {}, (resp: JsonRpcResponse) => {
|
|
||||||
if ("error" in resp) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setAudioInputAutoEnable(resp.result as boolean);
|
|
||||||
});
|
|
||||||
}, [send, setAudioInputAutoEnable]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (rpcDataChannel?.readyState !== "open") return;
|
if (rpcDataChannel?.readyState !== "open") return;
|
||||||
|
|
@ -827,6 +805,15 @@ export default function KvmIdRoute() {
|
||||||
});
|
});
|
||||||
}, [rpcDataChannel?.readyState, send, setHdmiState]);
|
}, [rpcDataChannel?.readyState, send, setHdmiState]);
|
||||||
|
|
||||||
|
// Load audio input auto-enable preference from backend
|
||||||
|
useEffect(() => {
|
||||||
|
if (rpcDataChannel?.readyState !== "open") return;
|
||||||
|
send("getAudioInputAutoEnable", {}, (resp: JsonRpcResponse) => {
|
||||||
|
if ("error" in resp) return;
|
||||||
|
setAudioInputAutoEnable(resp.result as boolean);
|
||||||
|
});
|
||||||
|
}, [rpcDataChannel?.readyState, send, setAudioInputAutoEnable]);
|
||||||
|
|
||||||
const [needLedState, setNeedLedState] = useState(true);
|
const [needLedState, setNeedLedState] = useState(true);
|
||||||
|
|
||||||
// request keyboard led state from the device
|
// request keyboard led state from the device
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,11 @@ import { DeviceStatus } from "@routes/welcome-local";
|
||||||
import { DEVICE_API } from "@/ui.config";
|
import { DEVICE_API } from "@/ui.config";
|
||||||
import api from "@/api";
|
import api from "@/api";
|
||||||
import { m } from "@localizations/messages.js";
|
import { m } from "@localizations/messages.js";
|
||||||
|
import { useSettingsStore } from "@/hooks/stores";
|
||||||
|
|
||||||
const loader: LoaderFunction = async () => {
|
const loader: LoaderFunction = async () => {
|
||||||
|
useSettingsStore.getState().resetMicrophoneState();
|
||||||
|
|
||||||
const res = await api
|
const res = await api
|
||||||
.GET(`${DEVICE_API}/device/status`)
|
.GET(`${DEVICE_API}/device/status`)
|
||||||
.then(res => res.json() as Promise<DeviceStatus>);
|
.then(res => res.json() as Promise<DeviceStatus>);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
|
import { useEffect } from "react";
|
||||||
import { useLocation, useSearchParams } from "react-router";
|
import { useLocation, useSearchParams } from "react-router";
|
||||||
|
|
||||||
import { m } from "@localizations/messages.js";
|
import { m } from "@localizations/messages.js";
|
||||||
import AuthLayout from "@components/AuthLayout";
|
import AuthLayout from "@components/AuthLayout";
|
||||||
|
import { useSettingsStore } from "@/hooks/stores";
|
||||||
|
|
||||||
export default function LoginRoute() {
|
export default function LoginRoute() {
|
||||||
const [sq] = useSearchParams();
|
const [sq] = useSearchParams();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const deviceId = sq.get("deviceId") || location.state?.deviceId;
|
const deviceId = sq.get("deviceId") || location.state?.deviceId;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
useSettingsStore.getState().resetMicrophoneState();
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (deviceId) {
|
if (deviceId) {
|
||||||
return (
|
return (
|
||||||
<AuthLayout
|
<AuthLayout
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue