aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2020-03-03 16:25:53 -0500
committerbob <bcz@cs.brown.edu>2020-03-03 16:25:53 -0500
commitfcbef60fdd7edbff63ad190f500f7b67a6dafa71 (patch)
treeb4f38b5775e256b9950a252d7015676c02b94ed5 /src
parent8b71007c98f3c5f5092d15c6ce91142729bcec22 (diff)
restored audio recording.
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/DocumentTypes.ts2
-rw-r--r--src/client/documents/Documents.ts10
-rw-r--r--src/client/views/nodes/AudioBox.scss2
-rw-r--r--src/client/views/nodes/AudioBox.tsx60
-rw-r--r--src/server/DashUploadUtils.ts2
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}).`));