Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/BloomBrowserUI/bookEdit/bloomField/BloomField.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/// <reference path="../../typings/jquery/jquery.d.ts" />
/// <reference path="../../typings/ckeditor/ckeditor.d.ts" />

import AudioRecording from "../toolbox/talkingBook/audioRecording";
import { createValidXhtmlUniqueId } from "../js/xhtmlIdUtils";
import { get, post } from "../../utils/bloomApi";
import BloomMessageBoxSupport from "../../utils/bloomMessageBoxSupport";
import { tryProcessHyperlink } from "./hyperlinks";
Expand Down Expand Up @@ -520,7 +520,7 @@ export default class BloomField {
nodelist.forEach(
(span: Element, key: number, parent: NodeListOf<Element>) => {
const oldId = span.getAttribute("id");
const newId = AudioRecording.createValidXhtmlUniqueId();
const newId = createValidXhtmlUniqueId();
span.setAttribute("id", newId);
post(`audio/copyAudioFile?oldId=${oldId}&newId=${newId}`);
},
Expand Down
24 changes: 24 additions & 0 deletions src/BloomBrowserUI/bookEdit/js/audioUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// taken out of audioRecording.ts to avoid the need for other
// files to import that big file just to use a little bit of
// code.

import axios, { AxiosResponse } from "axios";

export const kAnyRecordingApiUrl = "/bloom/api/audio/checkForAnyRecording?ids=";

export async function audioExistsForIdsAsync(ids: string[]): Promise<boolean> {
try {
const response: AxiosResponse<any> = await axios.get(
`${kAnyRecordingApiUrl}${ids}`,
);
return doesNarrationExist(response);
} catch {
return false;
}
}

// Given a response from kAnyRecordingApiUrl, determines whether narration audio exists
// for any of the specified IDs.
export function doesNarrationExist(response: AxiosResponse<any>): boolean {
return response && response.data === true;
}
2 changes: 1 addition & 1 deletion src/BloomBrowserUI/bookEdit/js/bloomImages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { updateCanvasElementClass } from "../toolbox/canvas/canvasElementDomUtil
import { farthest } from "../../utils/elementUtils";
import { EditableDivUtils } from "./editableDivUtils";
import { playingBloomGame } from "../toolbox/games/DragActivityTabControl";
import { kPlaybackOrderContainerClass } from "../toolbox/talkingBook/audioRecording";
import { kPlaybackOrderContainerClass } from "./talkingBookMarkupConstants";
import { getWorkspaceBundleExports } from "./workspaceFrames";
import {
changeImage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { BloomTooltip } from "../../../react_components/BloomToolTip";
import { useL10n } from "../../../react_components/l10nHooks";
import { kBloomDisabledOpacity } from "../../../utils/colorUtils";
import { getAsync, useApiObject } from "../../../utils/bloomApi";
import AudioRecording from "../../toolbox/talkingBook/audioRecording";
import { audioExistsForIdsAsync } from "../audioUtils";
import { getAudioSentencesOfVisibleEditables } from "bloom-player";
import { canvasElementControlRegistry } from "../../toolbox/canvas/canvasElementControlRegistry";
import { buildCanvasElementControlRegistryContext } from "../../toolbox/canvas/buildCanvasElementControlRegistryContext";
Expand Down Expand Up @@ -156,7 +156,7 @@ const CanvasElementContextControls: React.FunctionComponent<{
props.canvasElement,
);
const ids = audioSentences.map((sentence) => sentence.id);
AudioRecording.audioExistsForIdsAsync(ids)
audioExistsForIdsAsync(ids)
.then((audioExists) => {
setTextHasAudio(audioExists);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
kBloomButtonClass,
kImageFitModeAttribute,
} from "../../toolbox/canvas/canvasElementConstants";
import AudioRecording from "../../toolbox/talkingBook/audioRecording";
import { createValidXhtmlUniqueId } from "../xhtmlIdUtils";
import { postData, postJson } from "../../../utils/bloomApi";
import { cloneCanvasElementHtmlStructure } from "./canvasElementCloneCleanup";

Expand Down Expand Up @@ -369,7 +369,7 @@ export class CanvasElementDuplication {
sourceId: string,
copiedElement: Element,
): void {
const newId = AudioRecording.createValidXhtmlUniqueId();
const newId = createValidXhtmlUniqueId();
copiedElement.setAttribute("id", newId);
void copyAudioFileAsync(sourceId, newId); // we don't need to wait for this to finish
const duration = sourceElement.getAttribute("data-duration");
Expand Down
26 changes: 26 additions & 0 deletions src/BloomBrowserUI/bookEdit/js/talkingBookChecksum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// taken out of audioRecording.ts to avoid the need for other
// files to import that big file just to use a little bit of
// code.

import { getMd5 } from "../toolbox/talkingBook/md5Util";

export function getChecksum(message: string): string {
if (message === null || message === undefined) {
// should not normally happen, but seems to in tests.
// The function is supposed to return a string, and I don't want to change
// all the callers, so making it return a string that's a bit unique so if
// we ever see it in production we can search for it.
return "undefind";
}
// Vertical line character ("|") acts as a phrase delimiter in Talking Books.
// To perform phrase-level recording, the user can insert a temporary "|" character where he wants a phrase split to happen.
// This is now recognized in the list of sentence delimiters, so it will be broken up as an audio-sentence.
// Then the user records the audio.
// Then the user deletes the vertical line characters.
// Now the text should be the desired final state, and audio recordings are possible at a sub-sentence level.
// However, we don't want the sentence markup to be updated because the checksums differ (since a character was deleted).
//
// Thus, our checksum function needs to ignore the vertical line character when computing the checksum.
const adjustedMessage = message.replace("|", "");
return getMd5(adjustedMessage);
}
8 changes: 8 additions & 0 deletions src/BloomBrowserUI/bookEdit/js/talkingBookMarkupConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// taken out of audioRecording.ts to avoid the need for other
// files to import that big file just to use a little bit of
// code.

export const kPlaybackOrderContainerClass: string =
"bloom-playbackOrderControlsContainer";

export const kAudioCurrent = "ui-audioCurrent";
14 changes: 14 additions & 0 deletions src/BloomBrowserUI/bookEdit/js/xhtmlIdUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// taken out of audioRecording.ts to avoid the need for other
// files to import that big file just to use a little bit of
// code.

import { EditableDivUtils } from "./editableDivUtils";

export function createValidXhtmlUniqueId(): string {
let newId = EditableDivUtils.createUuid();
if (/^\d/.test(newId)) {
newId = "i" + newId; // valid ID in XHTML can't start with digit
}

return newId;
}
42 changes: 1 addition & 41 deletions src/BloomBrowserUI/bookEdit/toolbox/ToolboxRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,6 @@ const toolIconPathByToolId: Record<string, string> = {
"/bloom/bookEdit/toolbox/impairmentVisualizer/blind-eye-white.svg",
};

const legacyToolSubPathByToolId: Record<string, string> = {
talkingBook: "talkingBook/talkingBookToolboxTool.html",
};

const toolboxHeaderIconStyles = css`
width: 16px;
height: 16px;
Expand Down Expand Up @@ -154,7 +150,6 @@ const makeSectionFromToolId = (toolId: string): ToolboxSection => {
id: toolId,
englishLabel: labelInfo.englishLabel,
l10nKey: labelInfo.l10nKey,
legacyToolHtmlSubPath: legacyToolSubPathByToolId[toolId],
};
};

Expand Down Expand Up @@ -261,12 +256,7 @@ const ensureReactToolBodyElement = (
}

const normalizedToolId = normalizeToolId(toolId);
// Do not create elements for legacy tools; they load their content from
// legacyToolHtmlSubPath and their makeRootElement() implementations throw
// "Method not implemented." if called directly.
if (legacyToolSubPathByToolId[normalizedToolId]) {
return undefined;
}

const tool = getMasterToolList().find((candidate) => {
return candidate.id() === normalizedToolId;
});
Expand Down Expand Up @@ -457,36 +447,6 @@ export const ToolboxRoot: React.FunctionComponent = () => {
return;
}

const legacyToolHtmlSubPath = legacyToolSubPathByToolId[toolId];
if (legacyToolHtmlSubPath) {
try {
const legacyToolBodyHtml = await loadLegacyToolBodyHtml({
id: toolId,
englishLabel: "",
l10nKey: "",
legacyToolHtmlSubPath,
});
setSections((previousSections) =>
previousSections.map((section) => {
if (section.id !== toolId) {
return section;
}
return {
...section,
legacyToolBodyHtml,
};
}),
);
} catch (error) {
hydratedToolIds.current.delete(toolId);
console.error(
`Failed to load legacy toolbox HTML for ${toolId}.`,
error,
);
}
return;
}

hydratedToolIds.current.delete(toolId);
}, []);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ import {
playSound,
showDialogToChooseSoundFileAsync,
} from "../games/GameTool";
import AudioRecording from "../talkingBook/audioRecording";
import { showTalkingBookTool } from "../talkingBook/showTalkingBookTool";
import { showLinkTargetChooserDialog } from "../../../react_components/LinkTargetChooser/LinkTargetChooserDialogLauncher";
import { kBloomBlue } from "../../../bloomMaterialUITheme";
import {
Expand Down Expand Up @@ -334,7 +334,7 @@ const makeChooseAudioMenuItemForText = (
englishLabel: "Use Talking Book Tool",
onSelect: () => {
runtime.closeMenu(false);
AudioRecording.showTalkingBookTool();
showTalkingBookTool();
},
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from "../../../react_components/BloomDialog/commonDialogComponents";
import { useL10n } from "../../../react_components/l10nHooks";
import { getAsync, postBoolean, postJsonAsync } from "../../../utils/bloomApi";
import { kAudioCurrent } from "../../js/talkingBookMarkupConstants";
import { AdjustTimingsControl, TimedTextSegment } from "./AdjustTimingsControl";
import {
getUrlPrefixFromWindowHref,
Expand Down
22 changes: 22 additions & 0 deletions src/BloomBrowserUI/bookEdit/toolbox/talkingBook/IAudioRecorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
// anything implementation-specific.

import { RecordingMode } from "./recordingMode"; // only holds the RecordingMode enum
import { TalkingBookUiState } from "./TalkingBookUiState";

export interface IAudioRecorder {
autoSegmentBasedOnTextLength: () => number[];
markAudioSplit: () => void;
insertSegmentMarker(): void;
setShowPlaybackOrder(isOn: boolean): Promise<void>;
setShowingImageDescriptions: (boolean) => void;
setRecordingMode(recordingMode: RecordingMode): Promise<void>;
handleImportRecordingClick(): void;
removeRecordingSetup: () => void;
getUpdateMarkupAction: () => Promise<() => void>;
setupForRecordingAsync: () => Promise<void>;
Expand All @@ -22,4 +27,21 @@ export interface IAudioRecorder {
getPageDocBody: () => HTMLElement | null;
getCurrentTextBox: () => HTMLElement | null;
recordingMode: RecordingMode;
setLevelCanvas(canvas: HTMLCanvasElement | null): void;
closeDeviceSelectMenu(): void;
changeInputDevice(): void;
setInputDevice(device: any): void;
startRecordCurrentAsync(): Promise<void>;
endRecordCurrentAsync(): Promise<void>;
togglePlayCurrentAsync(): Promise<void>;
playESpeakPreview(): void;
showAdjustTimingsDialog(): Promise<void>;
moveToNextAudioElement(): Promise<void>;
moveToPrevAudioElementAsync(): Promise<void>;
clearRecordingAsync(): Promise<void>;
listenAsync(canvasToExclude?: HTMLElement): Promise<void>;
getTalkingBookUiState(): TalkingBookUiState;
registerStateListener(
listener: (state: TalkingBookUiState) => void,
): () => void;
}
Loading