diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a2c0a9e6..1ea21c6c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -507,3 +507,64 @@ jobs: --latest \ --title "$TAG" fi + + - name: Refresh the docs /download page + # Only a stable release changes what /releases/latest resolves to, so a + # pre-release would rebuild the site to byte-identical output. + # + # Dispatched against main on purpose: the github-pages environment only + # permits `main` to deploy, so docs.yml's old `on: release` trigger ran + # with a tag ref and failed its deploy every time. See docs.yml. + if: ${{ steps.release.outputs.is_prerelease == 'false' }} + timeout-minutes: 20 + env: + GH_TOKEN: ${{ secrets.OPENSCREEN_RELEASE_TOKEN }} + run: | + latest_dispatch() { + gh run list \ + --repo "$GITHUB_REPOSITORY" \ + --workflow docs.yml \ + --event workflow_dispatch \ + --branch main \ + --limit 1 \ + --json databaseId \ + --jq '.[0].databaseId // empty' + } + + # `gh workflow run` prints nothing we can key off, so remember which + # dispatch was newest beforehand and wait for a different one to appear. + PREVIOUS_RUN_ID="$(latest_dispatch)" + gh workflow run docs.yml --ref main --repo "$GITHUB_REPOSITORY" + + RUN_ID="" + for _ in $(seq 1 30); do + sleep 5 + CANDIDATE="$(latest_dispatch)" + if [[ -n "$CANDIDATE" && "$CANDIDATE" != "$PREVIOUS_RUN_ID" ]]; then + RUN_ID="$CANDIDATE" + break + fi + done + + if [[ -z "$RUN_ID" ]]; then + echo "::error::Dispatched docs.yml but no new run appeared within 150s" + exit 1 + fi + + gh run watch "$RUN_ID" --repo "$GITHUB_REPOSITORY" --interval 15 || true + + CONCLUSION="$(gh run view "$RUN_ID" --repo "$GITHUB_REPOSITORY" --json conclusion --jq '.conclusion')" + case "$CONCLUSION" in + success) + echo "Docs rebuilt and deployed by run $RUN_ID" + ;; + cancelled) + # docs.yml cancels in-flight runs sharing a ref, so a push to main + # landing right now replaces this rebuild with a newer one. + echo "::warning::Docs run $RUN_ID was cancelled, most likely superseded by a newer main run" + ;; + *) + echo "::error::Docs run $RUN_ID concluded '$CONCLUSION' - /download may still list the previous release" + exit 1 + ;; + esac diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9b533f916..780963080 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,13 +12,17 @@ on: - "website/**" - ".github/workflows/docs.yml" # The /download page resolves the current release's assets at build time so it - # can link each platform to its actual file. Without this trigger that data - # would freeze at whatever the last website/** change saw, and the page would - # keep serving the previous version's binaries after every release. - # Pre-releases are skipped: /releases/latest ignores them, so the built output - # would be byte-identical. - release: - types: [published] + # can link each platform to its actual file. Without a post-release rebuild that + # data would freeze at whatever the last website/** change saw, and the page + # would keep serving the previous version's binaries after every release. + # + # That rebuild is a `workflow_dispatch` fired by build.yml once the release is + # published, NOT an `on: release` trigger. A release event runs with + # github.ref = refs/tags/vX.Y.Z, and the github-pages environment only allows + # `main` to deploy, so the deploy job failed on every stable release (it never + # surfaced earlier because pre-releases skipped the build entirely). Dispatching + # against main both satisfies that policy and publishes main's docs rather than + # the release branch's older snapshot. workflow_dispatch: # Cancel in-flight runs on the same ref so fast follow-up pushes @@ -34,9 +38,6 @@ jobs: build: name: Build site runs-on: ubuntu-latest - # A pre-release does not change what /releases/latest resolves to, so - # rebuilding for one would burn a run to produce identical output. - if: github.event_name != 'release' || github.event.release.prerelease == false steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: @@ -66,7 +67,7 @@ jobs: needs: build if: >- (github.event_name == 'push' && github.ref == 'refs/heads/main') - || github.event_name == 'release' + || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} diff --git a/package.json b/package.json index 9fdf8bf93..6768369e2 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,15 @@ "npm": "10.9.4" }, "author": { - "name": "Siddharth Vaddem", - "url": "https://github.com/siddharthvaddem" + "name": "Etienne Lescot", + "url": "https://github.com/EtienneLescot" }, + "contributors": [ + { + "name": "Siddharth Vaddem", + "url": "https://github.com/siddharthvaddem" + } + ], "maintainers": [ { "name": "Etienne Lescot", diff --git a/src/cli/CliCaptionsRunner.tsx b/src/cli/CliCaptionsRunner.tsx index 620210ee2..a87f729e2 100644 --- a/src/cli/CliCaptionsRunner.tsx +++ b/src/cli/CliCaptionsRunner.tsx @@ -11,7 +11,7 @@ import { } from "@/components/video-editor/projectPersistence"; import type { AnnotationRegion, TrimRegion } from "@/components/video-editor/types"; import { extractMono16kFromVideoUrl } from "@/lib/captioning/extractMono16k"; -import { transcribeMono16kToSegments } from "@/lib/captioning/transcribe"; +import { type SttRendererStatus, transcribeMono16kToSegments } from "@/lib/captioning/transcribe"; import type { CliCaptionsRequest, CliDoneResult } from "@/lib/cliContracts"; import { nativeBridgeClient } from "@/native"; import { captionSegmentsToAnnotationRegions } from "./captionAnnotations"; @@ -61,8 +61,14 @@ async function runCaptions(request: CliCaptionsRequest): Promise const trimMs = Math.round(trimSec * 1000); const trimRegionsForTranscribe = shiftTrimRegionsMsForCaptionBuffer(trimRegions, trimMs); + // `onStatus` now fires once per transcribed chunk, not once per phase, so log + // only on a phase change — otherwise a long transcription spams the CLI with + // one identical line per chunk. + let loggedPhase: SttRendererStatus["phase"] | null = null; const transcribeOptions = { - onStatus: (phase: "model" | "transcribe") => { + onStatus: ({ phase }: SttRendererStatus) => { + if (phase === loggedPhase) return; + loggedPhase = phase; window.electronAPI.cliLog( "info", phase === "model" ? "Loading caption model…" : "Transcribing…",