Skip to content
This repository was archived by the owner on Apr 18, 2024. It is now read-only.

Commit 235ae39

Browse files
authored
fix: LSDV-4711: CORS error accessing previously annotated data (#1299)
* fix: LSDV-4711: CORS error accessing previously annotated data * allow audio v3 to resetSource of a previously working url as the presigned url may have expired * retry paragraph audio source if it has previously loaded but encountered an error, as this may be a presigned url requiring refresh * retry Video if the source previously loaded but now is in error, could just require refresh of the presigned url
1 parent fbc9e92 commit 235ae39

File tree

10 files changed

+130
-16
lines changed

10 files changed

+130
-16
lines changed

src/components/ImageView/Image.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { observer } from 'mobx-react';
22
import { forwardRef, useCallback, useMemo } from 'react';
33
import { Block, Elem } from '../../utils/bem';
4+
import { FF_LSDV_4711, isFF } from '../../utils/feature-flags';
45
import messages from '../../utils/messages';
56
import { ErrorMessage } from '../ErrorMessage/ErrorMessage';
67
import './Image.styl';
@@ -64,6 +65,10 @@ const ImageProgress = observer(({
6465
) : null;
6566
});
6667

68+
const imgDefaultProps = {};
69+
70+
if (isFF(FF_LSDV_4711)) imgDefaultProps.crossOrigin = 'anonymous';
71+
6772
const ImageRenderer = observer(forwardRef(({
6873
src,
6974
onLoad,
@@ -78,6 +83,7 @@ const ImageRenderer = observer(forwardRef(({
7883

7984
return (
8085
<img
86+
{...imgDefaultProps}
8187
ref={ref}
8288
alt="image"
8389
src={src}

src/components/ImageView/ImageView.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@ import ResizeObserver from '../../utils/resize-observer';
2020
import { debounce } from '../../utils/debounce';
2121
import Constants from '../../core/Constants';
2222
import { fixRectToFit } from '../../utils/image';
23-
import { FF_DEV_1285, FF_DEV_1442, FF_DEV_3077, FF_DEV_3793, FF_DEV_4081, FF_LSDV_4583, FF_LSDV_4583_6, isFF } from '../../utils/feature-flags';
23+
import { FF_DEV_1285, FF_DEV_1442, FF_DEV_3077, FF_DEV_3793, FF_DEV_4081, FF_LSDV_4583, FF_LSDV_4583_6, FF_LSDV_4711, isFF } from '../../utils/feature-flags';
2424
import { Pagination } from '../../common/Pagination/Pagination';
2525
import { Image } from './Image';
2626

2727
Konva.showWarnings = false;
2828

2929
const hotkeys = Hotkey('Image');
30+
const imgDefaultProps = {};
31+
32+
if (isFF(FF_LSDV_4711)) imgDefaultProps.crossOrigin = 'anonymous';
3033

3134
const splitRegions = (regions) => {
3235
const brushRegions = [];
@@ -1080,6 +1083,7 @@ export default observer(
10801083
<div className={styles.gallery}>
10811084
{item.images.map((src, i) => (
10821085
<img
1086+
{...imgDefaultProps}
10831087
alt=""
10841088
key={src}
10851089
src={src}

src/components/VideoCanvas/VideoCanvas.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { forwardRef, memo, MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
22
import { Block, Elem } from '../../utils/bem';
3+
import { FF_LSDV_4711, isFF } from '../../utils/feature-flags';
34
import { clamp, isDefined } from '../../utils/utilities';
45
import './VideoCanvas.styl';
56
import { MAX_ZOOM, MIN_ZOOM } from './VideoConstants';
@@ -35,6 +36,7 @@ type VideoProps = {
3536
onFrameChange?: (frame: number, length: number) => void,
3637
onEnded?: () => void,
3738
onResize?: (dimensions: VideoDimentions) => void,
39+
onError?: (error: any) => void,
3840
}
3941

4042
type PanOptions = {
@@ -92,6 +94,7 @@ export const VideoCanvas = memo(forwardRef<VideoRef, VideoProps>((props, ref) =>
9294
const contextRef = useRef<CanvasRenderingContext2D | null>();
9395
const videoRef = useRef<HTMLVideoElement>();
9496
const supportedFileTypeRef = useRef<boolean|null>(null);
97+
const hasLoadedRef = useRef<boolean>(false);
9598

9699
const canvasWidth = useMemo(() => props.width ?? 600, [props.width]);
97100
const canvasHeight = useMemo(() => props.height ?? 600, [props.height]);
@@ -187,6 +190,7 @@ export const VideoCanvas = memo(forwardRef<VideoRef, VideoProps>((props, ref) =>
187190
if (!playing) updateFrame(true);
188191

189192
if (video.networkState === video.NETWORK_IDLE) {
193+
hasLoadedRef.current = true;
190194
setBuffering(false);
191195
} else {
192196
setBuffering(true);
@@ -224,6 +228,24 @@ export const VideoCanvas = memo(forwardRef<VideoRef, VideoProps>((props, ref) =>
224228
props.onPause?.();
225229
}, [props.onEnded]);
226230

231+
const handleVideoError = useCallback(() => {
232+
if (!isFF(FF_LSDV_4711)) return;
233+
234+
const video = videoRef.current;
235+
236+
if (video?.error && hasLoadedRef.current) {
237+
hasLoadedRef.current = false;
238+
239+
// If the video errored after loading, we can try to reload it
240+
// as it may have been a temporary network issue or signed url that expired
241+
video.load();
242+
} else if (video) {
243+
// If the video never loaded and errored, we can't do anything about it
244+
// so report it to the consumer
245+
props.onError?.(video.error);
246+
}
247+
}, [props.onError]);
248+
227249
const handleAnimationFrame = () => {
228250
updateFrame();
229251

@@ -551,6 +573,7 @@ export const VideoCanvas = memo(forwardRef<VideoRef, VideoProps>((props, ref) =>
551573
onPlaying={handleVideoPlaying}
552574
onWaiting={handleVideoWaiting}
553575
onEnded={handleVideoEnded}
576+
onError={handleVideoError}
554577
/>
555578
</Block>
556579
);

src/components/VideoCanvas/VirtualVideo.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { DetailedHTMLProps, forwardRef, useCallback, useEffect, useRef, VideoHTMLAttributes } from 'react';
22
import InfoModal from '../../components/Infomodal/Infomodal';
3+
import { FF_LSDV_4711, isFF } from '../../utils/feature-flags';
34

45

56
type VirtualVideoProps = DetailedHTMLProps<VideoHTMLAttributes<HTMLVideoElement>, HTMLVideoElement> & {
@@ -91,6 +92,8 @@ export const VirtualVideo = forwardRef<HTMLVideoElement, VirtualVideoProps>((pro
9192
videoEl.controls = false;
9293
videoEl.preload = 'auto';
9394

95+
if (isFF(FF_LSDV_4711)) videoEl.crossOrigin = 'anonymous';
96+
9497
Object.assign(videoEl.style, {
9598
top: '-9999px',
9699
width: 0,

src/lib/AudioUltra/Controls/Player.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ export class Player extends Destructable {
130130
init(audio: WaveformAudio) {
131131
this.audio = audio;
132132
this.audio.on('canplay', this.handleCanPlay);
133+
this.audio.on('resetSource', this.handleResetSource);
133134
if (this.audio.el) {
134135
this.audio.el.addEventListener('play', this.handlePlayed);
135136
this.audio.el.addEventListener('pause', this.handlePaused);
@@ -180,6 +181,18 @@ export class Player extends Destructable {
180181
this.updateCurrentTime(true);
181182
};
182183

184+
private handleResetSource = async () => {
185+
if (!this.audio?.el) return;
186+
187+
const wasPlaying = this.playing;
188+
189+
this.stop();
190+
this.audio.el.load();
191+
192+
if (wasPlaying) this.play();
193+
};
194+
195+
183196
private handleCanPlay = () => {
184197
this.bufferResolve?.();
185198
};

src/lib/AudioUltra/Media/WaveformAudio.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { FF_LSDV_4711, isFF } from '../../../utils/feature-flags';
12
import { Events } from '../Common/Events';
23
import { __DEBUG__ } from '../Common/Utils';
34
import { audioDecoderPool } from './AudioDecoderPool';
@@ -15,6 +16,7 @@ export interface WaveformAudioOptions {
1516
interface WaveformAudioEvents {
1617
decodingProgress: (chunk: number, total: number) => void;
1718
canplay: () => void;
19+
resetSource: () => void;
1820
}
1921

2022
export class WaveformAudio extends Events<WaveformAudioEvents> {
@@ -33,6 +35,7 @@ export class WaveformAudio extends Events<WaveformAudioEvents> {
3335
private decoderType: 'ffmpeg' | 'webaudio' = 'ffmpeg';
3436
private src?: string;
3537
private mediaResolve?: () => void;
38+
private hasLoadedSource = false;
3639

3740
constructor(options: WaveformAudioOptions) {
3841
super();
@@ -182,6 +185,9 @@ export class WaveformAudio extends Events<WaveformAudioEvents> {
182185
this.el.preload = 'auto';
183186
this.el.setAttribute('data-testid', 'waveform-audio');
184187
this.el.style.display = 'none';
188+
189+
if (isFF(FF_LSDV_4711)) this.el.crossOrigin = 'anonymous';
190+
185191
document.body.appendChild(this.el);
186192

187193
this.mediaPromise = new Promise((resolve, reject) => {
@@ -190,22 +196,31 @@ export class WaveformAudio extends Events<WaveformAudioEvents> {
190196
});
191197

192198
this.el.addEventListener('canplaythrough', this.mediaReady);
193-
this.el.addEventListener('error', this.mediaReady);
199+
this.el.addEventListener('error', this.mediaError);
194200
this.loadMedia();
195201
}
196202

197-
mediaReady = async (e: any) => {
198-
if (e.type === 'error') {
199-
this.mediaReject?.(this.el?.error);
203+
mediaError = () => {
204+
// If this source has already loaded, we will retry the source url
205+
if (isFF(FF_LSDV_4711) && this.hasLoadedSource && this.el) {
206+
this.hasLoadedSource = false;
207+
this.invoke('resetSource');
200208
} else {
201-
if (this.mediaResolve) {
202-
this.mediaResolve?.();
203-
this.mediaResolve = undefined;
204-
}
205-
this.invoke('canplay');
209+
// otherwise it's an unrecoverable error
210+
this.mediaReject?.(this.el?.error);
206211
}
207212
};
208213

214+
mediaReady = () => {
215+
if (this.mediaResolve) {
216+
this.mediaResolve?.();
217+
this.mediaResolve = undefined;
218+
}
219+
220+
this.hasLoadedSource = true;
221+
this.invoke('canplay');
222+
};
223+
209224
/**
210225
* Load the media element with the audio source and begin an initial playback buffer
211226
*/

src/tags/object/Image/Image.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { RectRegionModel } from '../../../regions/RectRegion';
1414
import * as Tools from '../../../tools';
1515
import ToolsManager from '../../../tools/Manager';
1616
import { parseValue } from '../../../utils/data';
17-
import { FF_DEV_3377, FF_DEV_3666, FF_DEV_3793, FF_DEV_4081, FF_LSDV_4583, FF_LSDV_4583_6, isFF } from '../../../utils/feature-flags';
17+
import { FF_DEV_3377, FF_DEV_3666, FF_DEV_3793, FF_DEV_4081, FF_LSDV_4583, FF_LSDV_4583_6, FF_LSDV_4711, isFF } from '../../../utils/feature-flags';
1818
import { guidGenerator } from '../../../utils/unique';
1919
import { clamp, isDefined } from '../../../utils/utilities';
2020
import ObjectBase from '../Base';
@@ -282,6 +282,8 @@ const Model = types.model({
282282
get imageCrossOrigin() {
283283
const value = self.crossorigin.toLowerCase();
284284

285+
if (isFF(FF_LSDV_4711) && (!value || value === 'none')) return 'anonymous';
286+
285287
if (!isFF(FF_DEV_4081)) {
286288
return null;
287289
} else if (!value || value === 'none') {

src/tags/object/Paragraphs/HtxParagraphs.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@ import React, { Component } from 'react';
22
import { inject, observer } from 'mobx-react';
33

44
import ObjectTag from '../../../components/Tags/Object';
5-
import { FF_DEV_2669, FF_DEV_2918, FF_LSDV_3012, isFF } from '../../../utils/feature-flags';
5+
import { FF_DEV_2669, FF_DEV_2918, FF_LSDV_3012, FF_LSDV_4711, isFF } from '../../../utils/feature-flags';
66
import { findNodeAt, matchesSelector, splitBoundaries } from '../../../utils/html';
77
import { isSelectionContainsSpan } from '../../../utils/selection-tools';
88
import styles from './Paragraphs.module.scss';
99
import { AuthorFilter } from './AuthorFilter';
1010
import { Phrases } from './Phrases';
1111

12+
const audioDefaultProps = {};
13+
14+
if (isFF(FF_LSDV_4711)) audioDefaultProps.crossOrigin = 'anonymous';
15+
1216
class HtxParagraphsView extends Component {
1317
_regionSpanSelector = '.htx-highlight';
1418

@@ -414,6 +418,7 @@ class HtxParagraphsView extends Component {
414418
<ObjectTag item={item} className={'lsf-paragraphs'}>
415419
{withAudio && (
416420
<audio
421+
{...audioDefaultProps}
417422
controls={item.showplayer && !item.syncedAudio}
418423
className={styles.audio}
419424
src={item.audio}
@@ -424,6 +429,8 @@ class HtxParagraphsView extends Component {
424429
ref: item.getRef(),
425430
})}
426431
onEnded={item.reset}
432+
onError={item.handleError}
433+
onCanPlay={item.handleCanPlay}
427434
/>
428435
)}
429436
{isFF(FF_DEV_2669) && <AuthorFilter item={item} />}

src/tags/object/Paragraphs/model.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import { SyncMixin } from '../../../mixins/SyncMixin';
1010
import { ParagraphsRegionModel } from '../../../regions/ParagraphsRegion';
1111
import Utils from '../../../utils';
1212
import { parseValue } from '../../../utils/data';
13-
import { FF_DEV_2461, FF_DEV_2669, FF_DEV_2918, FF_DEV_3666, FF_LSDV_3012, isFF } from '../../../utils/feature-flags';
13+
import { FF_DEV_2461, FF_DEV_2669, FF_DEV_2918, FF_DEV_3666, FF_LSDV_3012, FF_LSDV_4711, isFF } from '../../../utils/feature-flags';
1414
import messages from '../../../utils/messages';
1515
import { clamp, isDefined, isValidObjectURL } from '../../../utils/utilities';
1616
import ObjectBase from '../Base';
1717
import styles from './Paragraphs.module.scss';
1818

1919
const isFFDev2461 = isFF(FF_DEV_2461);
20+
const isFFLsdv4711 = isFF(FF_LSDV_4711);
2021

2122
/**
2223
* The `Paragraphs` tag displays paragraphs of text on the labeling interface. Use to label dialogue transcripts for NLP and NER projects.
@@ -350,6 +351,10 @@ const OldPlayAndSync = types.model()
350351
let audioStopHandler = null;
351352
let endDuration = 0;
352353
let currentId = -1;
354+
let currentSourceTime = 0;
355+
let hasLoadedSource = false;
356+
let reloadingSource = false;
357+
let wasPlayingId = -1;
353358

354359
function stop() {
355360
const audio = audioRef.current;
@@ -393,8 +398,41 @@ const OldPlayAndSync = types.model()
393398
}
394399
},
395400

401+
handleError() {
402+
if (!isFFLsdv4711) return;
403+
404+
const audio = audioRef.current;
405+
406+
// if the source has succesfully loaded before, we can try to reload it
407+
// as it may be an expired presigned url or temporary network issue
408+
if (audio && hasLoadedSource) {
409+
hasLoadedSource = false;
410+
reloadingSource = true;
411+
wasPlayingId = self.playingId;
412+
currentSourceTime = isNaN(audio.currentTime) ? currentSourceTime : audio.currentTime;
413+
stop();
414+
audio.load();
415+
}
416+
},
417+
418+
handleCanPlay() {
419+
if (!isFFLsdv4711) return;
420+
421+
const audio = audioRef.current;
422+
423+
hasLoadedSource = true;
424+
425+
if (audio && reloadingSource) {
426+
reloadingSource = false;
427+
audio.currentTime = currentSourceTime;
428+
429+
if (wasPlayingId > -1) self.play(wasPlayingId);
430+
}
431+
},
432+
396433
handleSyncSeek(time) {
397434
if (audioRef.current) {
435+
currentSourceTime = time;
398436
audioRef.current.currentTime = time;
399437
}
400438
},
@@ -662,6 +700,4 @@ const paragraphModelMixins = [
662700
ParagraphsLoadingModel,
663701
].filter(Boolean);
664702

665-
export const ParagraphsModel = types.compose('ParagraphsModel',
666-
...paragraphModelMixins,
667-
);
703+
export const ParagraphsModel = types.compose('ParagraphsModel', ...paragraphModelMixins);

src/utils/feature-flags.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ export const FF_LSDV_4701 = 'fflag_feat_front_lsdv_4701_audio_default_decoder_ff
212212
*/
213213
export const FF_LSDV_4659 = 'fflag_feat_front_lsdv_4659_skipduplicates_060323_short';
214214

215+
/**
216+
* Fixes how presigned urls are generated and accessed to remove possibility of CORS errors.
217+
*/
218+
export const FF_LSDV_4711 = 'fflag_fix_all_lsdv_4711_cors_errors_accessing_task_data_short';
219+
215220
/**
216221
* Preventing creating duplicates in TextArea results with "skipDuplicates" parameter during editing.
217222
* It also prevent creating new history steps on every change during editing textarea results.

0 commit comments

Comments
 (0)