diff --git a/src/servers/api/file-browser/router.ts b/src/servers/api/file-browser/router.ts
index 1ffd6f03..f97af2a2 100644
--- a/src/servers/api/file-browser/router.ts
+++ b/src/servers/api/file-browser/router.ts
@@ -849,14 +849,14 @@ router.post('/extract-audio', async (ctx) => {
const s = await stat(absPath);
if (s.isDirectory()) throw errors.BAD_REQUEST('Cannot extract audio from a directory');
+ // No caching: the extraction is always redone and overwrites the previous result. The output still
+ // lives under the user's data dir because this endpoint backs the file viewer's Extract Audio
+ // button, which plays it rather than saving it beside the video — the Extract Audio task is what
+ // writes into the user's folders.
const userDataDir = getUserDataDir(user.email);
const { dir, name } = parsePath(filePath.replace(/^\/+/, ''));
- const cacheRel = dir ? `cache/audio/${dir}/${name}.mp3` : `cache/audio/${name}.mp3`;
- const cacheAbs = resolve(userDataDir, cacheRel);
-
- if (existsSync(cacheAbs)) {
- return ctx.json({ audioPath: cacheRel, audioRoot: 'user-data', cached: true });
- }
+ const outRel = dir ? `cache/audio/${dir}/${name}.mp3` : `cache/audio/${name}.mp3`;
+ const cacheAbs = resolve(userDataDir, outRel);
await mkdir(dirname(cacheAbs), { recursive: true });
@@ -871,7 +871,7 @@ router.post('/extract-audio', async (ctx) => {
throw errors.BAD_REQUEST(stderr.trim() || 'Audio extraction failed');
}
- return ctx.json({ audioPath: cacheRel, audioRoot: 'user-data', cached: false });
+ return ctx.json({ audioPath: outRel, audioRoot: 'user-data', cached: false });
});
// Extract archive (zip, tar, 7z, rar) into a sibling folder
diff --git a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileGrid.tsx b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileGrid.tsx
index cf1855aa..6d95ca2d 100644
--- a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileGrid.tsx
+++ b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileGrid.tsx
@@ -62,7 +62,6 @@ export const FileGrid = ({ fileBrowserManager }: FileGridProps) => {
setRenamingName,
handleReadAloud,
handleOcr,
- handleExtractAudio,
handleExtract,
handlePlay,
getMatchingTaskGroups,
@@ -180,7 +179,6 @@ export const FileGrid = ({ fileBrowserManager }: FileGridProps) => {
onRenamingChange={setRenamingName}
onReadAloud={handleReadAloud}
onOcr={handleOcr}
- onExtractAudio={handleExtractAudio}
onExtract={handleExtract}
onPlay={handlePlay}
taskGroups={getMatchingTaskGroups(entry.name, entry.type)}
diff --git a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileItem.tsx b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileItem.tsx
index e1bcfdc5..96ac5892 100644
--- a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileItem.tsx
+++ b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/components/FileItem.tsx
@@ -13,7 +13,6 @@ import {
LayoutGrid,
Volume2,
ScanText,
- AudioLines,
FolderArchive,
ClipboardCopy,
Music,
@@ -64,7 +63,6 @@ export type FileItemProps = {
onRenamingChange: (name: string | null) => void;
onReadAloud: (entry: DirEntry) => void;
onOcr: (entry: DirEntry) => void;
- onExtractAudio: (entry: DirEntry) => void;
onExtract: (entry: DirEntry) => void;
onPlay: (entry: DirEntry) => void;
taskGroups: TaskGroup[];
@@ -96,7 +94,6 @@ type MenuItemsProps = {
onDownload: (e: DirEntry) => void;
onReadAloud: (e: DirEntry) => void;
onOcr: (e: DirEntry) => void;
- onExtractAudio: (e: DirEntry) => void;
onExtract: (e: DirEntry) => void;
onPlay: (e: DirEntry) => void;
onCut: () => void;
@@ -116,7 +113,6 @@ const DropdownMenuItems = ({
onDownload,
onReadAloud,
onOcr,
- onExtractAudio,
onExtract,
onPlay,
onCut,
@@ -128,12 +124,11 @@ const DropdownMenuItems = ({
const fileType = entry.type === 'file' ? getFileType(entry.name) : null;
const showReadAloud = fileType === 'markdown' || fileType === 'code' || fileType === 'text';
const showOcr = fileType === 'image';
- const showExtractAudio = fileType === 'video';
const showExtract = fileType === 'archive';
const showPlay = fileType === 'audio' || entry.type === 'directory';
const hasTasks = taskGroups.length > 0;
- const hasActions = showPlay || showReadAloud || showOcr || showExtractAudio || showExtract || hasTasks;
+ const hasActions = showPlay || showReadAloud || showOcr || showExtract || hasTasks;
// Only nest when more than one category matched — a file usually matches a single category, and
// Run Task > Video > Convert would just add a hop.
const nestTasks = taskGroups.length > 1;
@@ -159,12 +154,6 @@ const DropdownMenuItems = ({
Extract Text (OCR)
)}
- {showExtractAudio && (
- onExtractAudio(entry)} className="cursor-pointer">
-
- Extract Audio
-
- )}
{showExtract && (
onExtract(entry)} className="cursor-pointer">
@@ -260,7 +249,6 @@ const ContextMenuItems = ({
onDownload,
onReadAloud,
onOcr,
- onExtractAudio,
onExtract,
onPlay,
onCut,
@@ -272,12 +260,11 @@ const ContextMenuItems = ({
const fileType = entry.type === 'file' ? getFileType(entry.name) : null;
const showReadAloud = fileType === 'markdown' || fileType === 'code' || fileType === 'text';
const showOcr = fileType === 'image';
- const showExtractAudio = fileType === 'video';
const showExtract = fileType === 'archive';
const showPlay = fileType === 'audio' || entry.type === 'directory';
const hasTasks = taskGroups.length > 0;
- const hasActions = showPlay || showReadAloud || showOcr || showExtractAudio || showExtract || hasTasks;
+ const hasActions = showPlay || showReadAloud || showOcr || showExtract || hasTasks;
// Only nest when more than one category matched — a file usually matches a single category, and
// Run Task > Video > Convert would just add a hop.
const nestTasks = taskGroups.length > 1;
@@ -303,12 +290,6 @@ const ContextMenuItems = ({
Extract Text (OCR)
)}
- {showExtractAudio && (
- onExtractAudio(entry)} className="cursor-pointer">
-
- Extract Audio
-
- )}
{showExtract && (
onExtract(entry)} className="cursor-pointer">
@@ -513,7 +494,6 @@ export const FileItem = ({
onRenamingChange,
onReadAloud,
onOcr,
- onExtractAudio,
onExtract,
onPlay,
taskGroups,
@@ -595,7 +575,6 @@ export const FileItem = ({
onDownload,
onReadAloud,
onOcr,
- onExtractAudio,
onExtract,
onPlay,
onCut,
diff --git a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/useFileBrowserApp.ts b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/useFileBrowserApp.ts
index 9203de99..dc9f17b5 100644
--- a/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/useFileBrowserApp.ts
+++ b/src/workspaces/officerdev/src/apps/FileBrowser/FileBrowserApp/useFileBrowserApp.ts
@@ -457,18 +457,6 @@ export const useFileBrowserApp = (
}
};
- const handleExtractAudio = async (entry: DirEntry) => {
- const filePath = entryPath(entry.name);
- const toastId = toast.loading('Extracting audio from video...');
- try {
- const { audioPath, audioRoot } = await files.extractAudio(filePath);
- toast.dismiss(toastId);
- setSearchParams({ view: filePath, ephemeral: audioPath, ephemeralRoot: audioRoot });
- } catch {
- toast.error('Failed to extract audio from video', { id: toastId });
- }
- };
-
const handlePlay = (entry: DirEntry) => {
const filePath = entryPath(entry.name);
setSearchParams({ play: filePath });
@@ -765,7 +753,6 @@ export const useFileBrowserApp = (
handleCreateDashboardHere,
handleReadAloud,
handleOcr,
- handleExtractAudio,
handleExtract,
handlePlay,
handleGitClone,