`handleVideoResolutionChange` did all of its work inside `if (videoTrack)`,
including `saveVideoPublishResolution`. With the camera off there is no camera
publication, so choosing a resolution did nothing at all: it was neither applied
nor recorded, while the selector went on showing the value the user had just
picked. Turning the camera back on then published at the old resolution, and so
did the next session.
Found on a self-hosted instance: a user set the sending resolution with the
camera off, turned it back on, and the publisher kept sending 720p. Nothing in
the UI suggested the choice had been dropped.
Persist the choice first and unconditionally, then restart the track only when
there is one to restart.
Persisting alone is not enough within a session. `roomOptions` is only read by
`new Room(...)`, so a store update never reaches a room that is already built.
Sync the VideoDeviceControl with the userChoiesStore resolution, as we did for
the device id and the processor configuration.
The early return is the honest shape here: with no live track there is nothing
to await, and the defaults above already cover what happens next.
Introduce an in-app devtool that monitors WebRTC statistics in
real time and lets developers simulate various network scenarios,
including constraining the uplink and downlink bandwidth.
Makes it much easier to reproduce and investigate connectivity or
quality issues locally without depending on external tools.
The code was AI generated, and might contain some smell.
It's only enabled in dev, and not included in the production
build. Feel free to enhance it as needed.
Wire a TURN server into the local LiveKit server used by the dev
stack, so ICE negotiation has more candidate types available during
local testing.
Makes it easier to reproduce connectivity scenarios that would
otherwise only show up on stricter networks in production.
The summary `uv.lock` had not been updated in the last few releases.
The CI is now refactored to use `uv sync --locked`, which fails when
the lockfile is out of sync with `pyproject.toml`.
Regenerate the lockfile so `uv sync --locked` passes again.
The rule joins the list it fetches into an alternation, so any regex character
in a gitmoji entry would change what the pattern matches. re.escape treats each
one as a literal, which is what the match was always meant to do.
lint-git runs gitlint through uvx, which fetches it for that one command and
puts nothing in the runner's Python. lint-summary and test-summary sync from a
new src/summary/uv.lock, so the versions those jobs resolve are the versions in
the tree, which is what src/backend and src/agents already do. The agents sync
refuses source distributions, since that package installs no project of its own.
Follows suitenumerique/menshen#73.
The rule downloaded the gitmoji list with requests, so lint-git had to install
that package before the linter could run. urllib.request is in the standard
library and answers the same call, leaving one fewer package fetched on the
runner before the job's own command starts.
--no-sync already stops uv run resolving an environment of its own,
and that is what the version findings were about. Building is a
separate guarantee that nothing on the line carried, so --no-build
now says it outright. It is inert beside --no-sync, and the three
lint jobs are unchanged.
The repository is already called meet, so the file name said nothing
about what the workflow holds. The print-statement check now excludes
the whole workflows directory rather than one file by name: its own
grep carries the literal print(, so the rename would otherwise match
it on the deleted lines and fail the job.
A tag and a branch both move, so actions/checkout@v6 and
numerique-gouv/action-trivy-cache@main ran whatever the owner had last
pushed. Each of the 65 uses now names a 40-character commit, with the
version it resolved to in a trailing comment.
dependabot.yml keeps the hash and that comment moving together.
gitlint-core keeps the linter at 0.19.1, which the wheels-only pin was
resolving down to 0.18.0 through an sdist-only sh. --ignore-scripts
stops three npm ci, one yarn install and one npm install -g running
the scripts of what they fetch, and yarn is pinned to 1.22.22. curl
holds the dockerize download and its redirects to https.
A package could run arbitrary code on the runner while installing,
and the versions were resolved rather than taken from the pins.
The two pip calls now take wheels only, and uv run no longer
resolves an environment of its own.
The component exposes the local microphone and camera state so external
tools automating the frontend can read it. It does not expose whether
publication is allowed, so such a tool cannot tell a muted microphone from
one it is not permitted to unmute, and ends up offering a control that
silently does nothing.
Expose canPublishMicrophone and canPublishCamera in the event detail and as
data attributes, reusing the useCanPublishTrack hook that already gates the
web client controls. The permissions are part of the effect dependencies so
a mid-meeting permission change emits the event.
The make lint target did not cover the summary and agents components. Update
the linting workflow to include both services and harmonize Makefile target
names. Harmonize Docker compose user declarations.
Add Spanish to the language selection system on both trees, with 872
frontend phrases and 145 backend ones translated from the French source.
The frontend to backend language map gains it too, so a Spanish browser
does not write an empty language to the account on every load.
The Kyutai open-source model turned out not to be production-ready:
it caused disruptions in the production environment, especially on
long-running meeting sessions.
Switch to the Voxtral realtime model, which looks like a
credible competitor and behaves much better in our setup.
For now, the code handling the Voxtral realtime API lives directly
in the project. It could be extracted into an open-source package
later.
See PR #1277 for the full details of the implementation, proposed
by @cameldev.