Skip to content
Open
Show file tree
Hide file tree
Changes from all 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("|", "");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Only the first | is stripped. String.prototype.replace with a plain string argument stops after the first match, so a sentence like "Hello | World | test" produces checksum "Hello World | test" — the second pipe is retained. After the user deletes all delimiter characters the actual text becomes "Hello World test", so the checksums never agree and the audio markup is unnecessarily regenerated every session. Use a global regex to remove all occurrences.

Suggested change
const adjustedMessage = message.replace("|", "");
const adjustedMessage = message.replace(/\|/g, "");

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,7 +20,7 @@ import {
} from "../../../react_components/BloomDialog/commonDialogComponents";
import { useL10n } from "../../../react_components/l10nHooks";
import { getAsync, postBoolean, postJsonAsync } from "../../../utils/bloomApi";
import { kAudioCurrent } from "./audioRecording";
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