Skip to content

Edit Mode and Configure OpenAi Chat Completions - #52

Open
gunsleobezerra wants to merge 25 commits into
JayWebtech:mainfrom
gunsleobezerra:main
Open

gunsleobezerra wants to merge 25 commits into
JayWebtech:mainfrom
gunsleobezerra:main

Conversation

@gunsleobezerra

Copy link
Copy Markdown
  • Edit Mode
  • Vídeo Thumbs
  • OpenAI Compatible Custom API
  • Download Local Whisper Models

gunsleobezerra and others added 25 commits October 26, 2026 19:35
Snapshot of the work that was already sitting uncommitted on this branch,
split out first so the test commits that follow land on a buildable tree.

Backend:
- llm: generic OpenAI-compatible provider (URL normalization, JSON-mode
  payload toggle, SSE chunk parsing, chat-completion text extraction).
- media: ffmpeg/ffprobe probing, audio extraction, ASS caption rendering,
  progress reporting and path helpers.
- transcription: local Whisper (Python) + Deepgram normalization, model
  catalog/download progress, segment building.
- lib: caption styles (classic-outline, modern-box, vibrant-*), drawtext
  filter generation, SRT/ASS export, output slugging.
- new modules: hardware (encoder/GPU detection), naming (output naming),
  progress (pipeline progress events).
- db: schema migration for the new project fields.
- models: EnvironmentStatus/caption fields.

Tooling and docs:
- scripts/build-all.mjs plus build:all/linux/windows/android/ios scripts.
- release workflow: Android and iOS jobs.
- .env.example: OPENAI_BASE_URL / OPENAI_COMPATIBLE_* entries.
- pyproject.toml + poetry.lock for the Python (Whisper) side, bun.lock.
- .gitignore: ignore artifacts/, VS Code settings, Tauri generated schemas.
Sets up the frontend test harness:

- devDependencies: vitest, jsdom, @testing-library/react,
  @testing-library/jest-dom.
- vite.config.ts: `test` block (jsdom environment, globals, setup file,
  `src/**/*.{test,spec}.{ts,tsx}` as the test glob, CSS disabled).
- src/test/setup.ts: loads jest-dom matchers and unmounts React trees
  after every test so suites cannot leak DOM between cases.
- npm scripts: `test` (single run), `test:watch`, and `test:rust`
  (forwards to cargo test in src-tauri).
main.tsx was a single 2.2k-line file that mounted the root, so nothing in it
could be imported by a test. Split it into importable pieces:

- src/App.tsx: the whole component tree, exported as the default `App`.
- src/main.tsx: only creates the React root and renders `<App />`.
- src/lib/format.ts: pure helpers `formatModelSize`, `fileName`,
  `formatTime`, plus new `formatTimeRange` and `formatPercent`.
  `formatTime` now clamps negative/NaN/Infinity input to 0 instead of
  rendering `NaN:NaN`.
- src/components/common.tsx: `StatusPill`, `PipelineStep` and `EmptyState`.

No behavior change for the running app; the duplicated local copies in
main.tsx were removed in favor of the imports.
21 tests in three layers:

- src/lib/format.test.ts (10): byte-size formatting across B/KB/MB/GB
  boundaries, file-name extraction for POSIX and Windows paths,
  mm:ss vs h:mm:ss time output, NaN/Infinity/negative clamping, time
  ranges that collapse when start and end match, and percent rounding
  with clamping to 0..100.
- src/components/common.test.tsx (7): StatusPill label and tone mapping
  for every project status, PipelineStep active/done/pending styling,
  and EmptyState title/description/action rendering.
- src/App.integration.test.tsx (4): mounts the real `App` with
  `invoke`, `listen` and the dialog plugin mocked. Verifies the
  onboarding gate appears when the environment is not ready, the
  sidebar renders the project list, the empty state shows when there
  are no projects, and clicking a row loads its detail and transcript
  segment count.
`db`, `llm`, `media`, `progress` and `transcription` were private, so an
integration test under `src-tauri/tests/` could not reach them through
`autoshorts_lib`. Promote all of them to `pub mod`, matching `hardware` and
`naming` which were already public.

This only widens the library's API surface; the Tauri binary and its
commands are unaffected.
10 unit tests for `media`:

- progress parsing: microsecond values (`out_time_us`), `HH:MM:SS.mmm`
  clock values, and unknown/invalid keys being ignored instead of
  aborting the parse.
- `parse_clock`: seconds-only, minute, hour forms and malformed input.
- `ass_filter_arg`: escaping of `\ : ' ,` for the `subtitles` filter,
  falling back to the bare file name when the path has no parent, and
  `None` when there is no file name at all.
- `absolute_path`: leaves absolute paths alone, resolves relative ones
  against the current directory.
- `command_exists`: false for a binary that cannot exist.
12 unit tests for `transcription`:

- download progress: percentage parsing out of the model downloader
  output, plus lines without a percentage being ignored.
- Whisper stdout parsing: `[00:00:01.500 --> 00:00:03.000]` timestamps and
  the segment-end helper.
- `build_segments`: empty input yields no segments, consecutive words are
  joined until a break, and a sentence-ending word closes the segment.
- Deepgram: normalization of the alternative/words payload, and errors
  for a response with no alternative or no words.
- Whisper raw JSON: normalization, and an error when `segments` is absent.
- the bundled model catalog is non-empty and every entry has the fields
  the UI needs.
17 unit tests for `llm`:

- `normalize_chat_completions_url`: base URLs, `/v1` bases, URLs that
  already end in `/chat/completions`, and trailing slashes.
- `compact_segments`: one numbered line per segment.
- `parse_candidate_json` robustness against real model output: fenced
  ```json blocks, a root array, a single bare candidate object,
  alternative key names for start/end/score, clips shorter than the
  minimum being dropped, clips without a hook being dropped, loose string
  scores, clamping out-of-range scores, truncation to 10 candidates, and
  a hard error on invalid JSON.
- SSE: chunked `data:` lines are concatenated, non-chunk payloads and
  empty streams are rejected.
- `extract_completion_text`: all chat-completion content shapes, and
  `None` when there is no content.
- `body_snippet`: long bodies get truncated.
- `chat_completion_payload`: JSON mode toggles `response_format`.
14 unit tests for the helpers that live in `lib.rs`:

- `generate_srt`: three-word chunking, text concatenation, index numbers,
  clipping to the requested window, and an empty result outside it.
  (The window in the clipping test is 0.0..4.0 because the filter is
  `w.start < end_sec`, exclusive.)
- `format_srt_time` / `format_ass_time`: zero, sub-second rounding,
  minute and hour rollover, and the ASS centisecond field.
- `build_ass_captions`: header (Script Info / V4+ Styles / Events),
  one Dialogue line per word, and escaping of `{ } \` in the text.
- `project_output_slug`: keeps letters and digits, collapses separators,
  strips leading/trailing dashes, and never returns an empty slug.
- `validate_media_extension`: accepts the supported video/audio
  extensions case-insensitively and rejects the rest.
- `demo_transcript`: has words, segments and a matching duration.
`src-tauri/tests/db_integration.rs` drives `autoshorts_lib::db::Database`
against a real on-disk SQLite file instead of mocking it, so schema
migration, foreign keys and cascades are exercised for real.

12 tests:
- projects: create/read, detail starts empty and tracks the transcript,
  rename + status updates persist, delete cascades to candidates, clips
  and transcripts.
- transcripts: saving a second transcript replaces the first.
- candidates: `replace_candidates` ranks rows 1..n and auto-selects the
  top six, is idempotent when called twice, and
  `get_candidate_with_project` joins both tables.
- clips: selection toggling by rank, and `clip updates` keeping the
  existing render paths when `None` is passed.
- recovery: reopening the database resets clips stuck in `cutting`, and
  opening the same file twice reuses the existing schema.

The test file carries a small `TempDir` helper (env::temp_dir + an
`AtomicU64` suffix, cleaned up on `Drop`) so no extra dev-dependency is
needed.
New `test` job on ubuntu-22.04 that runs before anything is published:

1. checkout, Node 20, stable Rust toolchain, `Swatinem/rust-cache`
   scoped to `src-tauri`.
2. Linux system dependencies (webkit2gtk, gtk, libappindicator, ...)
   needed to compile the Tauri crate.
3. `npm ci`, `npm run build` (the Rust build embeds `dist/`, so it has to
   exist first), `npm test`.
4. `cargo test --all-targets` for the unit and integration suites.

`release`, `android` and `ios` now declare `needs: test`, so a red test
suite stops the whole matrix instead of shipping broken binaries.
Also fixes the `matrix` indentation under `release`.
Adds "4. Run the Tests" to the README with the commands and a table of
every test file:

- `npm test` / `npm run test:watch` for Vitest,
- `npm run test:rust` (or `cargo test`) for the Rust suites,
- the three frontend files and the five Rust files with what each covers.

Renumbers "Multi-Platform Build" to 5.
`naming::unique_path` appended `-2`, `-3`, … whenever the target file
already existed, so re-cutting a clip after editing its hook left the old
render behind and the project folder grew one duplicate per attempt.

Clip names are already deterministic (project slug + rank + hook +
timestamps), so the render path is now `clips_dir.join(file_name)`: a
re-cut overwrites the previous render instead of accumulating copies.
Removes `unique_path` and its two tests from `naming`.
bun.lock was committed, and tauri-action chooses the package manager from lockfiles (usesBun before usesNpm), so Windows runners without bun failed. Remove the lockfile, ignore it, pin packageManager to npm, assert it in CI, pass tauriScript explicitly, and only cut releases on v* tags so branch pushes no longer create a bogus 'main' release.
- scripts/build-all.mjs: remove alvos android/ios e helpers (androidSdk,
  ANDROID_RUST_TARGETS, installedRustTargets); adiciona alvo macos (dmg/app)
- package.json: build:android/build:ios -> build:macos
- README: matriz de plataformas e comandos atualizados
…dinâmico

Motor novo em `autoedit.rs`:
- `detect_silences` roda `ffmpeg silencedetect` na janela do clipe;
- `build_plan`/`plan_from_silences` transformam as pausas em lista de cortes,
  subtraindo da detecção tudo que o transcript diz ser fala (evita cortar voz
  baixa), aplicando padding nas bordas e alternando zoom punch-in nos trechos
  mantidos;
- `remap_cues` reescreve as legendas na linha do tempo comprimida, removendo as
  que caíram em pausa cortada e dividindo as que atravessam um corte.

Render (`media.rs`):
- `ClipRenderRequest` ganhou `has_audio` e `segments`;
- com cut list o ffmpeg usa `-filter_complex` (trim/atrim + setpts/atempo por
  segmento, `concat`, crop/scale e `ass` uma única vez no resultado final) em
  vez de `-vf`;
- `-t` passa a cobrir a janela de origem (não a duração de saída), senão os
  últimos segmentos eram truncados;
- punch-in é aplicado por segmento, antes do concat, porque depois não haveria
  mais como distinguir os trechos.

Persistência: `clip_edits.auto_edit_json` + `AutoEditSettings::sanitized`.

UI: painel "Auto-edit: cortar silêncios" no editor do clipe (toggle, limite de
silêncio, pausa mínima, respiro, modo cortar/acelerar, velocidade e zoom
dinâmico) com resumo em texto e persistência junto do resto do `ClipEdit`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant