mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-10 18:50:54 +00:00
Merge pull request #455 from apitiot/2021.2.3
ServerManager: waiting for completion of media upload
This commit is contained in:
commit
cfeaf3a9e5
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "psychojs",
|
"name": "psychojs",
|
||||||
"version": "2021.2.2",
|
"version": "2021.2.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Helps run in-browser neuroscience, psychology, and psychophysics experiments",
|
"description": "Helps run in-browser neuroscience, psychology, and psychophysics experiments",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -176,7 +176,7 @@ export class PsychoJS
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info("[PsychoJS] Initialised.");
|
this.logger.info("[PsychoJS] Initialised.");
|
||||||
this.logger.info("[PsychoJS] @version 2021.2.2");
|
this.logger.info("[PsychoJS] @version 2021.2.3");
|
||||||
|
|
||||||
// hide the initialisation message:
|
// hide the initialisation message:
|
||||||
jQuery("#root").addClass("is-ready");
|
jQuery("#root").addClass("is-ready");
|
||||||
|
@ -27,7 +27,7 @@ import { PsychoJS } from "./PsychoJS.js";
|
|||||||
*/
|
*/
|
||||||
export class ServerManager extends PsychObject
|
export class ServerManager extends PsychObject
|
||||||
{
|
{
|
||||||
/**
|
/****************************************************************************
|
||||||
* Used to indicate to the ServerManager that all resources must be registered (and
|
* Used to indicate to the ServerManager that all resources must be registered (and
|
||||||
* subsequently downloaded)
|
* subsequently downloaded)
|
||||||
*
|
*
|
||||||
@ -54,14 +54,14 @@ export class ServerManager extends PsychObject
|
|||||||
this._addAttribute("status", ServerManager.Status.READY);
|
this._addAttribute("status", ServerManager.Status.READY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* @typedef ServerManager.GetConfigurationPromise
|
* @typedef ServerManager.GetConfigurationPromise
|
||||||
* @property {string} origin the calling method
|
* @property {string} origin the calling method
|
||||||
* @property {string} context the context
|
* @property {string} context the context
|
||||||
* @property {Object.<string, *>} [config] the configuration
|
* @property {Object.<string, *>} [config] the configuration
|
||||||
* @property {Object.<string, *>} [error] an error message if we could not read the configuration file
|
* @property {Object.<string, *>} [error] an error message if we could not read the configuration file
|
||||||
*/
|
*/
|
||||||
/**
|
/****************************************************************************
|
||||||
* Read the configuration file for the experiment.
|
* Read the configuration file for the experiment.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#getConfiguration
|
* @name module:core.ServerManager#getConfiguration
|
||||||
@ -100,14 +100,14 @@ export class ServerManager extends PsychObject
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* @typedef ServerManager.OpenSessionPromise
|
* @typedef ServerManager.OpenSessionPromise
|
||||||
* @property {string} origin the calling method
|
* @property {string} origin the calling method
|
||||||
* @property {string} context the context
|
* @property {string} context the context
|
||||||
* @property {string} [token] the session token
|
* @property {string} [token] the session token
|
||||||
* @property {Object.<string, *>} [error] an error message if we could not open the session
|
* @property {Object.<string, *>} [error] an error message if we could not open the session
|
||||||
*/
|
*/
|
||||||
/**
|
/****************************************************************************
|
||||||
* Open a session for this experiment on the remote PsychoJS manager.
|
* Open a session for this experiment on the remote PsychoJS manager.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#openSession
|
* @name module:core.ServerManager#openSession
|
||||||
@ -190,13 +190,13 @@ export class ServerManager extends PsychObject
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* @typedef ServerManager.CloseSessionPromise
|
* @typedef ServerManager.CloseSessionPromise
|
||||||
* @property {string} origin the calling method
|
* @property {string} origin the calling method
|
||||||
* @property {string} context the context
|
* @property {string} context the context
|
||||||
* @property {Object.<string, *>} [error] an error message if we could not close the session (e.g. if it has not previously been opened)
|
* @property {Object.<string, *>} [error] an error message if we could not close the session (e.g. if it has not previously been opened)
|
||||||
*/
|
*/
|
||||||
/**
|
/****************************************************************************
|
||||||
* Close the session for this experiment on the remote PsychoJS manager.
|
* Close the session for this experiment on the remote PsychoJS manager.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#closeSession
|
* @name module:core.ServerManager#closeSession
|
||||||
@ -277,7 +277,7 @@ export class ServerManager extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Get the value of a resource.
|
* Get the value of a resource.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#getResource
|
* @name module:core.ServerManager#getResource
|
||||||
@ -316,7 +316,7 @@ export class ServerManager extends PsychObject
|
|||||||
return pathStatusData.data;
|
return pathStatusData.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Get the status of a resource.
|
* Get the status of a resource.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#getResourceStatus
|
* @name module:core.ServerManager#getResourceStatus
|
||||||
@ -343,7 +343,7 @@ export class ServerManager extends PsychObject
|
|||||||
return pathStatusData.status;
|
return pathStatusData.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Set the resource manager status.
|
* Set the resource manager status.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#setStatus
|
* @name module:core.ServerManager#setStatus
|
||||||
@ -376,7 +376,7 @@ export class ServerManager extends PsychObject
|
|||||||
return this._status;
|
return this._status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Reset the resource manager status to ServerManager.Status.READY.
|
* Reset the resource manager status to ServerManager.Status.READY.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#resetStatus
|
* @name module:core.ServerManager#resetStatus
|
||||||
@ -389,7 +389,7 @@ export class ServerManager extends PsychObject
|
|||||||
return this.setStatus(ServerManager.Status.READY);
|
return this.setStatus(ServerManager.Status.READY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Prepare resources for the experiment: register them with the server manager and possibly
|
* Prepare resources for the experiment: register them with the server manager and possibly
|
||||||
* start downloading them right away.
|
* start downloading them right away.
|
||||||
*
|
*
|
||||||
@ -525,7 +525,7 @@ export class ServerManager extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Block the experiment until the specified resources have been downloaded.
|
* Block the experiment until the specified resources have been downloaded.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#waitForResources
|
* @name module:core.ServerManager#waitForResources
|
||||||
@ -621,13 +621,13 @@ export class ServerManager extends PsychObject
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* @typedef ServerManager.UploadDataPromise
|
* @typedef ServerManager.UploadDataPromise
|
||||||
* @property {string} origin the calling method
|
* @property {string} origin the calling method
|
||||||
* @property {string} context the context
|
* @property {string} context the context
|
||||||
* @property {Object.<string, *>} [error] an error message if we could not upload the data
|
* @property {Object.<string, *>} [error] an error message if we could not upload the data
|
||||||
*/
|
*/
|
||||||
/**
|
/****************************************************************************
|
||||||
* Asynchronously upload experiment data to the pavlovia server.
|
* Asynchronously upload experiment data to the pavlovia server.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#uploadData
|
* @name module:core.ServerManager#uploadData
|
||||||
@ -692,7 +692,7 @@ export class ServerManager extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Asynchronously upload experiment logs to the pavlovia server.
|
* Asynchronously upload experiment logs to the pavlovia server.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#uploadLog
|
* @name module:core.ServerManager#uploadLog
|
||||||
@ -751,37 +751,40 @@ export class ServerManager extends PsychObject
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Asynchronously upload audio data to the pavlovia server.
|
* Synchronously or asynchronously upload audio data to the pavlovia server.
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#uploadAudioVideo
|
* @name module:core.ServerManager#uploadAudioVideo
|
||||||
* @function
|
* @function
|
||||||
* @public
|
* @public
|
||||||
* @param {Blob} audioBlob - the audio blob to be uploaded
|
* @param {Blob} mediaBlob - the audio or video blob to be uploaded
|
||||||
* @param {string} tag - additional tag
|
* @param {string} tag - additional tag
|
||||||
|
* @param {boolean} [waitForCompletion=false] - whether or not to wait for completion
|
||||||
|
* before returning
|
||||||
* @returns {Promise<ServerManager.UploadDataPromise>} the response
|
* @returns {Promise<ServerManager.UploadDataPromise>} the response
|
||||||
*/
|
*/
|
||||||
async uploadAudioVideo(audioBlob, tag)
|
async uploadAudioVideo(mediaBlob, tag, waitForCompletion = false)
|
||||||
{
|
{
|
||||||
const response = {
|
const response = {
|
||||||
origin: "ServerManager.uploadAudio",
|
origin: "ServerManager.uploadAudio",
|
||||||
context: "when uploading audio data for experiment: " + this._psychoJS.config.experiment.fullpath,
|
context: "when uploading media data for experiment: " + this._psychoJS.config.experiment.fullpath,
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (
|
if (this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER
|
||||||
this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER
|
|
||||||
|| this._psychoJS.config.experiment.status !== "RUNNING"
|
|| this._psychoJS.config.experiment.status !== "RUNNING"
|
||||||
|| this._psychoJS._serverMsg.has("__pilotToken")
|
|| this._psychoJS._serverMsg.has("__pilotToken"))
|
||||||
)
|
|
||||||
{
|
{
|
||||||
throw "audio recordings can only be uploaded to the server for experiments running on the server";
|
throw "media recordings can only be uploaded to the server for experiments running on the server";
|
||||||
}
|
}
|
||||||
|
|
||||||
this._psychoJS.logger.debug("uploading audio data for experiment: " + this._psychoJS.config.experiment.fullpath);
|
this._psychoJS.logger.debug(`uploading media data for experiment: ${this._psychoJS.config.experiment.fullpath}`);
|
||||||
this.setStatus(ServerManager.Status.BUSY);
|
this.setStatus(ServerManager.Status.BUSY);
|
||||||
|
|
||||||
|
// open pop-up dialog:
|
||||||
|
// TODO
|
||||||
|
|
||||||
// prepare the request:
|
// prepare the request:
|
||||||
const info = this.psychoJS.experiment.extraInfo;
|
const info = this.psychoJS.experiment.extraInfo;
|
||||||
const participant = ((typeof info.participant === "string" && info.participant.length > 0) ? info.participant : "PARTICIPANT");
|
const participant = ((typeof info.participant === "string" && info.participant.length > 0) ? info.participant : "PARTICIPANT");
|
||||||
@ -790,15 +793,15 @@ export class ServerManager extends PsychObject
|
|||||||
const filename = participant + "_" + experimentName + "_" + datetime + "_" + tag;
|
const filename = participant + "_" + experimentName + "_" + datetime + "_" + tag;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("audio", audioBlob, filename);
|
formData.append("media", mediaBlob, filename);
|
||||||
|
|
||||||
const url = this._psychoJS.config.pavlovia.URL
|
let url = this._psychoJS.config.pavlovia.URL
|
||||||
+ "/api/v2/experiments/" + this._psychoJS.config.gitlab.projectId
|
+ "/api/v2/experiments/" + this._psychoJS.config.gitlab.projectId
|
||||||
+ "/sessions/" + this._psychoJS.config.session.token
|
+ "/sessions/" + this._psychoJS.config.session.token
|
||||||
+ "/audio";
|
+ "/media";
|
||||||
|
|
||||||
// query the pavlovia server:
|
// query the server:
|
||||||
const response = await fetch(url, {
|
let response = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
mode: "cors",
|
mode: "cors",
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
@ -807,16 +810,58 @@ export class ServerManager extends PsychObject
|
|||||||
referrerPolicy: "no-referrer",
|
referrerPolicy: "no-referrer",
|
||||||
body: formData,
|
body: formData,
|
||||||
});
|
});
|
||||||
const jsonResponse = await response.json();
|
const postMediaResponse = await response.json();
|
||||||
|
this._psychoJS.logger.debug(`post media response: ${JSON.stringify(postMediaResponse)}`);
|
||||||
|
|
||||||
// deal with server errors:
|
// deal with server errors:
|
||||||
if (!response.ok)
|
if (!response.ok)
|
||||||
{
|
{
|
||||||
throw jsonResponse;
|
throw postMediaResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait until the upload has completed:
|
||||||
|
if (waitForCompletion)
|
||||||
|
{
|
||||||
|
if (!("uploadToken" in postMediaResponse))
|
||||||
|
{
|
||||||
|
throw "incorrect server response: missing uploadToken";
|
||||||
|
}
|
||||||
|
const uploadToken = postMediaResponse['uploadToken'];
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// wait a bit:
|
||||||
|
await new Promise(r =>
|
||||||
|
{
|
||||||
|
setTimeout(r, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
// check the status of the upload:
|
||||||
|
url = this._psychoJS.config.pavlovia.URL
|
||||||
|
+ "/api/v2/experiments/" + this._psychoJS.config.gitlab.projectId
|
||||||
|
+ "/sessions/" + this._psychoJS.config.session.token
|
||||||
|
+ "/media/" + uploadToken + "/status";
|
||||||
|
|
||||||
|
response = await fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
mode: "cors",
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "same-origin",
|
||||||
|
redirect: "follow",
|
||||||
|
referrerPolicy: "no-referrer"
|
||||||
|
});
|
||||||
|
const checkSatusResponse = await response.json();
|
||||||
|
this._psychoJS.logger.debug(`check upload status response: ${JSON.stringify(checkStatusResponse)}`);
|
||||||
|
|
||||||
|
if (("status" in checkStatusResponse) && checkStatusResponse["status"] === "COMPLETED")
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setStatus(ServerManager.Status.READY);
|
this.setStatus(ServerManager.Status.READY);
|
||||||
return jsonResponse;
|
return postMediaResponse;
|
||||||
}
|
}
|
||||||
catch (error)
|
catch (error)
|
||||||
{
|
{
|
||||||
@ -827,9 +872,9 @@ export class ServerManager extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* List the resources available to the experiment.
|
* List the resources available to the experiment.
|
||||||
|
*
|
||||||
* @name module:core.ServerManager#_listResources
|
* @name module:core.ServerManager#_listResources
|
||||||
* @function
|
* @function
|
||||||
* @private
|
* @private
|
||||||
@ -896,7 +941,7 @@ export class ServerManager extends PsychObject
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Download the specified resources.
|
* Download the specified resources.
|
||||||
*
|
*
|
||||||
* <p>Note: we use the [preloadjs library]{@link https://www.createjs.com/preloadjs}.</p>
|
* <p>Note: we use the [preloadjs library]{@link https://www.createjs.com/preloadjs}.</p>
|
||||||
@ -1124,7 +1169,7 @@ export class ServerManager extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Server event
|
* Server event
|
||||||
*
|
*
|
||||||
* <p>A server event is emitted by the manager to inform its listeners of either a change of status, or of a resource related event (e.g. download started, download is completed).</p>
|
* <p>A server event is emitted by the manager to inform its listeners of either a change of status, or of a resource related event (e.g. download started, download is completed).</p>
|
||||||
@ -1166,7 +1211,7 @@ ServerManager.Event = {
|
|||||||
STATUS: Symbol.for("STATUS"),
|
STATUS: Symbol.for("STATUS"),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Server status
|
* Server status
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#Status
|
* @name module:core.ServerManager#Status
|
||||||
@ -1191,7 +1236,7 @@ ServerManager.Status = {
|
|||||||
ERROR: Symbol.for("ERROR"),
|
ERROR: Symbol.for("ERROR"),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/****************************************************************************
|
||||||
* Resource status
|
* Resource status
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#ResourceStatus
|
* @name module:core.ServerManager#ResourceStatus
|
||||||
|
@ -369,9 +369,12 @@ export class Camera extends PsychObject
|
|||||||
* @name module:visual.Camera#upload
|
* @name module:visual.Camera#upload
|
||||||
* @function
|
* @function
|
||||||
* @public
|
* @public
|
||||||
* @param {string} tag an optional tag for the audio file
|
* @param {Object} options
|
||||||
|
* @param {string} options.tag an optional tag for the video file
|
||||||
|
* @param {boolean} [options.waitForCompletion= false] whether or not to wait for completion
|
||||||
|
* before returning
|
||||||
*/
|
*/
|
||||||
async upload({tag} = {})
|
async upload({ tag, waitForCompletion = false } = {})
|
||||||
{
|
{
|
||||||
// default tag: the name of this Camera object
|
// default tag: the name of this Camera object
|
||||||
if (typeof tag === "undefined")
|
if (typeof tag === "undefined")
|
||||||
@ -394,7 +397,7 @@ export class Camera extends PsychObject
|
|||||||
|
|
||||||
// upload the blob:
|
// upload the blob:
|
||||||
const videoBlob = new Blob(this._videoBuffer);
|
const videoBlob = new Blob(this._videoBuffer);
|
||||||
return this._psychoJS.serverManager.uploadAudioVideo(videoBlob, tag);
|
return this._psychoJS.serverManager.uploadAudioVideo(videoBlob, tag, waitForCompletion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user