From 887eed226bb8025d8c2310682080891146e35798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Mon, 24 Aug 2026 04:47:05 +0000 Subject: [PATCH] the transcription goes beside the audio, and nothing else moves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit it created a dated directory, moved the recording into it and wrote the markdown alongside. nobody asked for an archive. the thing you want from a transcription is the text next to the recording, which is exactly what the file viewer's transcribe button did — POST with saveNextTo, sibling .md, done. overwrites a previous transcription of the same file on purpose: it is derived, running it again means you wanted it again, and a -2 beside it is only a second file to choose between. inline: true, so the file browser runs it and waits rather than making a background job out of a call that takes seconds. Co-Authored-By: Claude Opus 5 Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043 --- tasks/transcribe-audio-file/TASK.md | 12 +++---- tasks/transcribe-audio-file/index.ts | 48 ++++++---------------------- 2 files changed, 15 insertions(+), 45 deletions(-) diff --git a/tasks/transcribe-audio-file/TASK.md b/tasks/transcribe-audio-file/TASK.md index 497de9c..71c0a08 100644 --- a/tasks/transcribe-audio-file/TASK.md +++ b/tasks/transcribe-audio-file/TASK.md @@ -4,7 +4,7 @@ description: Transcribe an audio file to text over your own whisper.cpp server, category: Media version: 1 mode: script -inline: false +inline: true language: typescript tags: - audio @@ -38,11 +38,11 @@ model call spent re-deriving what the plugin already returns. (`GET /api/user/settings`). 2. `POST` the file to `/api/transcribe/transcribe`. The plugin detects the language and transcribes faithfully in it — see `plugins/transcribe/skills/transcribe/SKILL.md` for the contract. -3. Create a sibling directory named `_`, where `` is today in `YYYYMMDD` and `` - is derived from the original file's own name (not a generated title — a script task gets no model - turn to ask for one). -4. Move the original audio file into that directory and save the transcription beside it as a - Markdown file with the same base name. +3. Write the transcription beside the audio file, same name, `.md`. Nothing is moved and nothing is + created around it. + +It overwrites a previous transcription of the same file, deliberately: a transcription is derived, and +running it again means you wanted it again. ## Before it runs diff --git a/tasks/transcribe-audio-file/index.ts b/tasks/transcribe-audio-file/index.ts index c1ebe6a..d63bc92 100644 --- a/tasks/transcribe-audio-file/index.ts +++ b/tasks/transcribe-audio-file/index.ts @@ -7,7 +7,6 @@ // src/servers/api/tasks/task-api-env.ts). The transcribe plugin owns the Whisper contract; this script // only owns filing the result. -import { rename, mkdir } from 'node:fs/promises'; import { existsSync } from 'node:fs'; import { basename, dirname, join, extname } from 'node:path'; @@ -27,15 +26,6 @@ if (!apiUrl || !apiHost || !authToken) { const authHeaders = { Authorization: `Bearer ${authToken}`, Host: apiHost }; -const slugify = (name: string) => - name - .trim() - .toLowerCase() - .replace(/\s+/g, '-') - .replace(/[^a-z0-9-]/g, '') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - async function getSpokenLanguages(): Promise { try { const res = await fetch(`${apiUrl}/api/user/settings`, { headers: authHeaders }); @@ -66,17 +56,6 @@ async function transcribe(path: string, spokenLanguages: string[]): Promise<{ te return { text: json.text ?? '', detectedLanguage: json.detectedLanguage ?? 'unknown' }; } -/** `/`, walking `-2`, `-3`, … past a collision. Same shape as the file browser's own. */ -async function uniqueDir(parentDir: string, name: string): Promise { - let candidate = join(parentDir, name); - let n = 2; - while (existsSync(candidate)) { - candidate = join(parentDir, `${name}-${n}`); - n += 1; - } - return candidate; -} - async function main() { const path = filePath!; if (!existsSync(path)) { @@ -90,26 +69,17 @@ async function main() { const { text, detectedLanguage } = await transcribe(path, spokenLanguages); console.log(`Detected language: ${detectedLanguage}`); - const parentDir = dirname(path); - const ext = extname(path); - const base = basename(path, ext); - - const now = new Date(); - const date = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`; - // No model turn to ask for a title (see TASK.md), so the slug comes from the original filename rather - // than the transcription's contents. - const slug = slugify(base) || 'recording'; - - const archiveDir = await uniqueDir(parentDir, `${date}_${slug}`); - await mkdir(archiveDir, { recursive: true }); - - const archivedAudioPath = join(archiveDir, basename(path)); - await rename(path, archivedAudioPath); - - const transcriptPath = join(archiveDir, `${base}.md`); + // Beside the audio, same name, `.md`. Nothing is moved, nothing is created around it — this is the + // behaviour the file viewer's transcribe button had, and the one thing anybody wants from a + // transcription is the text next to the recording it came from. + // + // It overwrites a previous transcription of the same file deliberately: a transcription is derived, + // running it again means you wanted it again, and a `-2` beside it would only be a second file to + // read and choose between. + const transcriptPath = join(dirname(path), `${basename(path, extname(path))}.md`); await Bun.write(transcriptPath, text); - console.log(`Filed under ${archiveDir}`); + console.log(transcriptPath); } main().catch((err) => {