diff --git a/images/chromium-headful/Dockerfile b/images/chromium-headful/Dockerfile index 96901308..a4ba6e31 100644 --- a/images/chromium-headful/Dockerfile +++ b/images/chromium-headful/Dockerfile @@ -177,42 +177,17 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-ap apt-get update && \ apt-get -y upgrade && \ apt-get --no-install-recommends -y install \ + gpg \ gpg-agent \ # UI Requirements xvfb \ - xterm \ xdotool \ - scrot \ - imagemagick \ sudo \ mutter \ - # Python/pyenv reqs - build-essential \ - libssl-dev \ - zlib1g-dev \ - libbz2-dev \ - libreadline-dev \ - libsqlite3-dev \ - curl \ - git \ - libncursesw5-dev \ - xz-utils \ - tk-dev \ - libxml2-dev \ - libxmlsec1-dev \ - libffi-dev \ - liblzma-dev \ - # Network tools - net-tools \ - netcat \ - # PPA req - software-properties-common && \ # Userland apps - sudo add-apt-repository ppa:mozillateam/ppa && \ - sudo apt-get --no-install-recommends -y install \ - x11-apps \ - tint2 \ + curl \ wget \ + xz-utils \ xdg-utils \ libvulkan1 \ fontconfig \ @@ -254,7 +229,6 @@ RUN apt-get update && \ # install ffmpeg manually since the version available in apt is from the 4.x branch due to #drama. COPY --from=ffmpeg-downloader /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg -COPY --from=ffmpeg-downloader /usr/local/bin/ffprobe /usr/local/bin/ffprobe # runtime ENV USERNAME=root @@ -263,13 +237,13 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-ap set -eux; \ apt-get update; \ apt-get --no-install-recommends -y install \ - wget ca-certificates python2 supervisor xclip xdotool unclutter \ + wget ca-certificates supervisor xclip xdotool unclutter \ pulseaudio dbus-x11 xserver-xorg-video-dummy \ libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx7 \ x11-xserver-utils \ gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \ - gstreamer1.0-pulseaudio gstreamer1.0-omx; \ + gstreamer1.0-pulseaudio; \ # # install libxcvt0 (not available in debian:bullseye) ARCH=$(dpkg --print-architecture); \ @@ -291,12 +265,11 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-ap chown $USERNAME /var/log/neko/ /tmp/runtime-$USERNAME; \ chown -R $USERNAME:$USERNAME /home/$USERNAME; -# sqlite3 for debugging the cookies file; runtime libs are chrome-for-testing's listed deb.deps. +# runtime libs are chrome-for-testing's listed deb.deps. RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \ --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \ apt-get update -y && \ apt-get --no-install-recommends -y install \ - sqlite3 \ libasound2 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 \ libcups2 libdbus-1-3 libdrm2 libgbm1 libglib2.0-0 libgtk-3-0 \ libnspr4 libnss3 libpango-1.0-0 libxcomposite1 libxdamage1 \ @@ -333,8 +306,9 @@ RUN set -eux; \ ln -sf /usr/local/lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack; \ fi -# Install TypeScript, Playwright, Patchright, esbuild globally -RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm install -g typescript playwright-core patchright esbuild +# Install Playwright, Patchright, esbuild globally (esbuild is required at +# runtime by playwright-daemon.js for TypeScript transformation) +RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm install -g playwright-core patchright esbuild # Pre-install openssh-server and websocat for faster SSH setup via kernel CLI. # Only installed, NOT configured: no host keys, no custom config, service not enabled. diff --git a/images/chromium-headless/image/Dockerfile b/images/chromium-headless/image/Dockerfile index a27861b6..bb110170 100644 --- a/images/chromium-headless/image/Dockerfile +++ b/images/chromium-headless/image/Dockerfile @@ -143,9 +143,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-ap apt-get -yqq --no-install-recommends install \ ca-certificates \ curl \ - build-essential \ - libssl-dev \ - git \ + gpg \ gpg-agent \ dbus \ dbus-x11 \ @@ -159,7 +157,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-ap fonts-noto-cjk \ fonts-noto-color-emoji \ fonts-nanum \ - software-properties-common \ supervisor; \ fc-cache -f @@ -191,7 +188,6 @@ COPY shared/chromium-policies/managed/policy.json /etc/chromium/policies/managed # Install FFmpeg (latest static build) for the recording server COPY --from=ffmpeg-downloader /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg -COPY --from=ffmpeg-downloader /usr/local/bin/ffprobe /usr/local/bin/ffprobe # Remove upower to prevent spurious D-Bus activations and logs RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \ @@ -209,8 +205,9 @@ RUN set -eux; \ ln -sf /usr/local/lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack; \ fi -# Install TypeScript, Playwright, Patchright, esbuild globally -RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm install -g typescript playwright-core patchright esbuild +# Install Playwright, Patchright, esbuild globally (esbuild is required at +# runtime by playwright-daemon.js for TypeScript transformation) +RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm install -g playwright-core patchright esbuild # Pre-install openssh-server and websocat for faster SSH setup via kernel CLI. # Only installed, NOT configured: no host keys, no custom config, service not enabled. diff --git a/server/e2e/e2e_recording_audio_test.go b/server/e2e/e2e_recording_audio_test.go index 74651437..c3152528 100644 --- a/server/e2e/e2e_recording_audio_test.go +++ b/server/e2e/e2e_recording_audio_test.go @@ -449,41 +449,41 @@ func mp4Durations(t *testing.T, data []byte) (float64, float64) { recordingPath := filepath.Join(t.TempDir(), "recording.mp4") require.NoError(t, os.WriteFile(recordingPath, data, 0o644), "failed to write recording for duration analysis") + // Decode the first audio stream to null; ffmpeg reports the container + // duration from the input header and the decoded audio length as the final + // time= progress value. ffprobe is intentionally not shipped in the image. out, err := exec.Command( "docker", "run", "--rm", "-v", recordingPath+":/tmp/recording.mp4:ro", - "--entrypoint", "ffprobe", + "--entrypoint", "ffmpeg", headfulImage, - "-v", "error", - "-show_entries", "format=duration", - "-show_entries", "stream=codec_type,duration", - "-of", "json", - "/tmp/recording.mp4", + "-hide_banner", + "-i", "/tmp/recording.mp4", + "-map", "0:a:0", + "-f", "null", + "-", ).CombinedOutput() - require.NoError(t, err, "failed to probe recording durations: %s", string(out)) - - var probe struct { - Streams []struct { - CodecType string `json:"codec_type"` - Duration string `json:"duration"` - } `json:"streams"` - Format struct { - Duration string `json:"duration"` - } `json:"format"` - } - require.NoError(t, json.Unmarshal(out, &probe), "failed to parse ffprobe output") + require.NoError(t, err, "failed to analyze recording durations: %s", string(out)) - formatDuration, err := strconv.ParseFloat(probe.Format.Duration, 64) - require.NoError(t, err, "failed to parse format duration") + formatMatch := regexp.MustCompile(`Duration: (\d+):(\d+):(\d+(?:\.\d+)?)`).FindStringSubmatch(string(out)) + require.Len(t, formatMatch, 4, "failed to find container duration in ffmpeg output: %s", string(out)) + formatDuration := hmsToSeconds(t, formatMatch[1], formatMatch[2], formatMatch[3]) - for _, stream := range probe.Streams { - if stream.CodecType != "audio" { - continue - } - audioDuration, err := strconv.ParseFloat(stream.Duration, 64) - require.NoError(t, err, "failed to parse audio duration") - return formatDuration, audioDuration - } - t.Fatal("ffprobe did not report an audio stream") - return 0, 0 + timeMatches := regexp.MustCompile(`time=(\d+):(\d+):(\d+(?:\.\d+)?)`).FindAllStringSubmatch(string(out), -1) + require.NotEmpty(t, timeMatches, "failed to find audio duration in ffmpeg output: %s", string(out)) + last := timeMatches[len(timeMatches)-1] + audioDuration := hmsToSeconds(t, last[1], last[2], last[3]) + + return formatDuration, audioDuration +} + +func hmsToSeconds(t *testing.T, h, m, s string) float64 { + t.Helper() + hours, err := strconv.ParseFloat(h, 64) + require.NoError(t, err, "failed to parse hours") + minutes, err := strconv.ParseFloat(m, 64) + require.NoError(t, err, "failed to parse minutes") + seconds, err := strconv.ParseFloat(s, 64) + require.NoError(t, err, "failed to parse seconds") + return hours*3600 + minutes*60 + seconds }