diff options
author | bob <bcz@cs.brown.edu> | 2020-03-03 16:25:53 -0500 |
---|---|---|
committer | bob <bcz@cs.brown.edu> | 2020-03-03 16:25:53 -0500 |
commit | fcbef60fdd7edbff63ad190f500f7b67a6dafa71 (patch) | |
tree | b4f38b5775e256b9950a252d7015676c02b94ed5 /src | |
parent | 8b71007c98f3c5f5092d15c6ce91142729bcec22 (diff) |
restored audio recording.
Diffstat (limited to 'src')
-rw-r--r-- | src/client/documents/DocumentTypes.ts | 2 | ||||
-rw-r--r-- | src/client/documents/Documents.ts | 10 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.scss | 2 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.tsx | 60 | ||||
-rw-r--r-- | src/server/DashUploadUtils.ts | 2 |
5 files changed, 41 insertions, 35 deletions
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 2e5d6a055..e3ce8e798 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -11,7 +11,7 @@ export enum DocumentType { PDF = "pdf", IMPORT = "import", LINK = "link", - LINKDOC = "linkdoc", + LINKDB = "linkdb", BUTTON = "button", SLIDER = "slider", EXTENSION = "extension", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 49e7520f2..67e037286 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -239,7 +239,7 @@ export namespace Docs { layout: { view: LinkBox, dataField: data }, options: { _height: 150 } }], - [DocumentType.LINKDOC, { + [DocumentType.LINKDB, { data: new List<Doc>(), layout: { view: EmptyBox, dataField: data }, options: { childDropAction: "alias", title: "LINK DB" } @@ -327,7 +327,7 @@ export namespace Docs { * A collection of all links in the database. Ideally, this would be a search, but for now all links are cached here. */ export function MainLinkDocument() { - return Prototypes.get(DocumentType.LINKDOC); + return Prototypes.get(DocumentType.LINKDB); } /** @@ -459,7 +459,7 @@ export namespace Docs { const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey); const viewDoc = Doc.MakeDelegate(dataDoc, delegId); - AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "link to audio: " + d.title)); + viewDoc.type !== DocumentType.LINK && AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "link to audio: " + d.title)); return Doc.assign(viewDoc, delegateProps, true); } @@ -520,7 +520,9 @@ export namespace Docs { } export function AudioDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(new URL(url)), options); + const instance = InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(new URL(url)), options); + Doc.GetProto(instance).backgroundColor = ComputedField.MakeFunction("this._audioState === 'playing' ? 'green':'gray'"); + return instance; } export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) { diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index 3b19a6dba..0c363f0c1 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -9,7 +9,6 @@ width:20px; height:100%; display:inline-block; - background: gray; } .audiobox-control, .audiobox-control-interactive { top:0; @@ -37,7 +36,6 @@ position: relative; display: flex; padding-left: 2px; - border: gray solid 3px; .audiobox-player { margin-top:auto; margin-bottom:auto; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 62a479b2a..862578e40 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -47,30 +47,34 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume @observable private static _scrubTime = 0; @observable private _audioState: "unrecorded" | "recording" | "recorded" = "unrecorded"; - @observable private _playing = false; public static SetScrubTime = action((timeInMillisFrom1970: number) => AudioBox._scrubTime = timeInMillisFrom1970); public static ActiveRecordings: Doc[] = []; + @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); } + + componentWillUnmount() { + this._reactionDisposer?.(); + this._linkPlayDisposer?.(); + this._scrubbingDisposer?.(); + } componentDidMount() { runInAction(() => this._audioState = this.path ? "recorded" : "unrecorded"); this._linkPlayDisposer = reaction(() => this.layoutDoc.scrollToLinkID, scrollLinkId => { - scrollLinkId && DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => { - const la1 = l.anchor1 as Doc; - const linkTime = Doc.AreProtosEqual(la1, this.dataDoc) ? NumCast(l.anchor1Timecode) : NumCast(l.anchor2Timecode); - setTimeout(() => { this.playFrom(linkTime); Doc.linkFollowHighlight(l); }, 250); - }); - scrollLinkId && Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false); + if (scrollLinkId) { + DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => { + const linkTime = Doc.AreProtosEqual(l.anchor1 as Doc, this.dataDoc) ? NumCast(l.anchor1Timecode) : NumCast(l.anchor2Timecode); + setTimeout(() => { this.playFromTime(linkTime); Doc.linkFollowHighlight(l); }, 250); + }); + Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false); + } }, { fireImmediately: true }); this._reactionDisposer = reaction(() => SelectionManager.SelectedDocuments(), selected => { const sel = selected.length ? selected[0].props.Document : undefined; - this.Document.playOnSelect && sel && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFrom(DateCast(sel.creationTime).date.getTime()); + this.Document.playOnSelect && this.recordingStart && sel && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFromTime(DateCast(sel.creationDate).date.getTime()); }); - this._scrubbingDisposer = reaction(() => AudioBox._scrubTime, timeInMillisecondsFrom1970 => { - const start = DateCast(this.dataDoc[this.props.fieldKey + "-recordingStart"]); - start && this.playFrom((timeInMillisecondsFrom1970 - start.date.getTime()) / 1000); - }); + this._scrubbingDisposer = reaction(() => AudioBox._scrubTime, (time) => this.Document.playOnSelect && this.playFromTime(AudioBox._scrubTime)); } timecodeChanged = () => { @@ -94,9 +98,12 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume pause = action(() => { this._ele!.pause(); - this._playing = false; + this.props.Document._audioState = "paused"; }); + playFromTime = (absoluteTime: number) => { + this.recordingStart && this.playFrom((absoluteTime - this.recordingStart) / 1000); + } playFrom = (seekTimeInSeconds: number) => { if (this._ele && AudioBox.Enabled) { if (seekTimeInSeconds < 0) { @@ -104,19 +111,13 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume } else if (seekTimeInSeconds <= this._ele.duration) { this._ele.currentTime = seekTimeInSeconds; this._ele.play(); - runInAction(() => this._playing = true); + runInAction(() => this.props.Document._audioState = "playing"); } else { this.pause(); } } } - componentWillUnmount() { - this._reactionDisposer && this._reactionDisposer(); - this._linkPlayDisposer && this._linkPlayDisposer(); - this._scrubbingDisposer && this._scrubbingDisposer(); - } - updateRecordTime = () => { if (this._audioState === "recording") { @@ -142,10 +143,13 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume method: 'POST', body: formData }); - const files = await res.json(); - const url = Utils.prepend(files[0].path); - // upload to server with known URL - self.props.Document[self.props.fieldKey] = new AudioField(url); + const json = await res.json(); + json.map(async (file: any) => { + const path = file.result.accessPaths.agnostic.client; + const url = Utils.prepend(path); + // upload to server with known URL + self.props.Document[self.props.fieldKey] = new AudioField(url); + }); }; runInAction(() => self._audioState = "recording"); self._recordStart = new Date().getTime(); @@ -191,8 +195,8 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume } setRef = (e: HTMLAudioElement | null) => { - e && e.addEventListener("timeupdate", this.timecodeChanged); - e && e.addEventListener("ended", this.pause); + e?.addEventListener("timeupdate", this.timecodeChanged); + e?.addEventListener("ended", this.pause); this._ele = e; } @@ -214,14 +218,14 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume const interactive = this.active() ? "-interactive" : ""; return <div className={`audiobox-container`} onContextMenu={this.specificContextMenu} onClick={!this.path ? this.recordClick : undefined}> - <div className="audiobox-handle"></div> + <div className="audiobox-handle" style={{ backgroundColor: this._audioState === "recording" ? "green" : undefined }} /> {!this.path ? <button className={`audiobox-record${interactive}`} style={{ backgroundColor: this._audioState === "recording" ? "red" : "black" }}> {this._audioState === "recording" ? "STOP" : "RECORD"} </button> : <div className="audiobox-controls"> <div className="audiobox-player" onClick={this.onPlay}> - <div className="audiobox-playhead"> <FontAwesomeIcon style={{ width: "100%" }} icon={this._playing ? "pause" : "play"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div> + <div className="audiobox-playhead"> <FontAwesomeIcon style={{ width: "100%" }} icon={this.props.Document._audioState === "paused" ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div> <div className="audiobox-playhead" onClick={this.onStop}><FontAwesomeIcon style={{ width: "100%" }} icon="stop" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div> <div className="audiobox-timeline" onClick={e => e.stopPropagation()} onPointerDown={e => { diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index ea4c26ca2..cc3dd75a4 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -76,6 +76,8 @@ export namespace DashUploadUtils { if (applicationFormats.includes(format)) { return UploadPdf(file); } + default: // "blob": + return MoveParsedFile(file, Directory.videos); } console.log(red(`Ignoring unsupported file (${name}) with upload type (${type}).`)); |