Merge pull request #15868 from danielpetri1/capture-notes-toast
This commit is contained in:
commit
b837d95d98
@ -186,7 +186,7 @@ trait PresentationWithAnnotationsMsgHdlr extends RightsManagementTrait {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
|
||||
val envelope = BbbCoreEnvelope(PresentationPageConversionStartedEventMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(CaptureSharedNotesReqEvtMsg.NAME, meetingId, "not-used")
|
||||
val body = CaptureSharedNotesReqEvtMsgBody(m.parentMeetingId, m.meetingName, m.sequence)
|
||||
val body = CaptureSharedNotesReqEvtMsgBody(m.parentMeetingId, m.meetingName)
|
||||
val event = CaptureSharedNotesReqEvtMsg(header, body)
|
||||
|
||||
bus.outGW.send(BbbCommonEnvCoreMsg(envelope, event))
|
||||
@ -195,14 +195,14 @@ trait PresentationWithAnnotationsMsgHdlr extends RightsManagementTrait {
|
||||
def handle(m: PadCapturePubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
|
||||
val userId: String = "system"
|
||||
val jobId: String = RandomStringGenerator.randomAlphanumericString(16);
|
||||
val jobId: String = s"${m.body.breakoutId}-notes" // Used as the temporaryPresentationId upon upload
|
||||
val jobType = "PadCaptureJob"
|
||||
val filename = s"${m.body.meetingName}-notes"
|
||||
val filename = m.body.filename
|
||||
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
||||
|
||||
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(m.body.parentMeetingId, userId, presentationUploadToken, filename))
|
||||
|
||||
val exportJob = new ExportJob(jobId, jobType, filename, m.body.padId, "", true, List(m.body.sequence), m.body.parentMeetingId, presentationUploadToken)
|
||||
val exportJob = new ExportJob(jobId, jobType, filename, m.body.padId, "", true, List(), m.body.parentMeetingId, presentationUploadToken)
|
||||
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
|
||||
|
||||
bus.outGW.send(job)
|
||||
|
@ -117,7 +117,7 @@ case class PadUpdateCmdMsgBody(groupId: String, name: String, text: String)
|
||||
// pads -> apps
|
||||
object PadCapturePubMsg { val NAME = "PadCapturePubMsg" }
|
||||
case class PadCapturePubMsg(header: BbbCoreHeaderWithMeetingId, body: PadCapturePubMsgBody) extends PadStandardMsg
|
||||
case class PadCapturePubMsgBody(parentMeetingId: String, breakoutId: String, padId: String, meetingName: String, sequence: Int)
|
||||
case class PadCapturePubMsgBody(parentMeetingId: String, breakoutId: String, padId: String, filename: String)
|
||||
|
||||
// client -> apps
|
||||
object PadPinnedReqMsg { val NAME = "PadPinnedReqMsg" }
|
||||
|
@ -39,6 +39,6 @@ case class NewPresAnnFileAvailableEvtMsgBody(fileURI: String, presId: String)
|
||||
|
||||
object CaptureSharedNotesReqEvtMsg { val NAME = "CaptureSharedNotesReqEvtMsg" }
|
||||
case class CaptureSharedNotesReqEvtMsg(header: BbbClientMsgHeader, body: CaptureSharedNotesReqEvtMsgBody) extends BbbCoreMsg
|
||||
case class CaptureSharedNotesReqEvtMsgBody(parentMeetingId: String, meetingName: String, sequence: Int)
|
||||
case class CaptureSharedNotesReqEvtMsgBody(parentMeetingId: String, meetingName: String)
|
||||
|
||||
// ------------ akka-apps to client ------------
|
||||
|
@ -10,9 +10,6 @@
|
||||
"imagemagick": "/usr/bin/convert",
|
||||
"pdftocairo": "/usr/bin/pdftocairo"
|
||||
},
|
||||
"captureNotes": {
|
||||
"timeout": 5000
|
||||
},
|
||||
"collector": {
|
||||
"pngWidthRasterizedSlides": 2560
|
||||
},
|
||||
|
@ -117,7 +117,7 @@ async function sleep(ms) {
|
||||
/** Export shared notes via bbb-pads in the desired format
|
||||
* @param {Integer} retries - Number of retries to get the shared notes
|
||||
*/
|
||||
async function collectSharedNotes(retries) {
|
||||
async function collectSharedNotes(retries = 3) {
|
||||
/** One of the following formats is supported:
|
||||
etherpad / html / pdf / txt / doc / odf */
|
||||
|
||||
@ -128,12 +128,6 @@ async function collectSharedNotes(retries) {
|
||||
const notes_endpoint = `${config.bbbPadsAPI}/p/${padId}/export/${notesFormat}`;
|
||||
const filePath = path.join(dropbox, filename);
|
||||
|
||||
const [sequence] = JSON.parse(exportJob.pages);
|
||||
const timeout = (sequence - 1) * config.captureNotes.timeout;
|
||||
|
||||
// Wait for the bbb-pads API to be available
|
||||
await sleep(timeout);
|
||||
|
||||
const finishedDownload = promisify(stream.finished);
|
||||
const writer = fs.createWriteStream(filePath);
|
||||
|
||||
@ -142,13 +136,15 @@ async function collectSharedNotes(retries) {
|
||||
method: 'GET',
|
||||
url: notes_endpoint,
|
||||
responseType: 'stream',
|
||||
timeout: timeout,
|
||||
});
|
||||
response.data.pipe(writer);
|
||||
await finishedDownload(writer);
|
||||
} catch (err) {
|
||||
if (retries > 0) {
|
||||
logger.info(`Retrying ${jobId} in ${timeout}ms...`);
|
||||
if (retries > 0 && err?.response?.status == 429) {
|
||||
// Wait for the bbb-pads API to be available due to rate limiting
|
||||
const backoff = err.response.headers['retry-after'] * 1000;
|
||||
logger.info(`Retrying ${jobId} in ${backoff}ms...`);
|
||||
await sleep(backoff);
|
||||
return collectSharedNotes(retries - 1);
|
||||
} else {
|
||||
logger.error(`Could not download notes in job ${jobId}`);
|
||||
@ -162,6 +158,6 @@ async function collectSharedNotes(retries) {
|
||||
switch (exportJob.jobType) {
|
||||
case 'PresentationWithAnnotationExportJob': return collectAnnotationsFromRedis();
|
||||
case 'PresentationWithAnnotationDownloadJob': return collectAnnotationsFromRedis();
|
||||
case 'PadCaptureJob': return collectSharedNotes(3);
|
||||
case 'PadCaptureJob': return collectSharedNotes();
|
||||
default: return logger.error(`Unknown job type ${exportJob.jobType}`);
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ async function upload(filePath) {
|
||||
{headers: formData.getHeaders()});
|
||||
logger.info(`Upload of job ${exportJob.jobId} returned ${res.data}`);
|
||||
} catch (error) {
|
||||
return logger.error(`Could upload job ${exportJob.jobId}: ${error}`);
|
||||
return logger.error(`Could not upload job ${exportJob.jobId}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ function breakouts() {
|
||||
sequence: 1,
|
||||
shortName: 1,
|
||||
timeRemaining: 1,
|
||||
captureNotes: 1,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -5,11 +5,10 @@ export default function captureSharedNotes({ body }, meetingId) {
|
||||
check(body, Object);
|
||||
check(meetingId, String);
|
||||
|
||||
const { parentMeetingId, meetingName, sequence } = body;
|
||||
const { parentMeetingId, meetingName } = body;
|
||||
|
||||
check(parentMeetingId, String);
|
||||
check(meetingName, String);
|
||||
check(sequence, Number);
|
||||
|
||||
padCapture(meetingId, parentMeetingId, meetingName, sequence);
|
||||
padCapture(meetingId, parentMeetingId, meetingName);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import Pads from '/imports/api/pads';
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function padCapture(meetingId, parentMeetingId, meetingName, sequence) {
|
||||
export default function padCapture(meetingId, parentMeetingId, meetingName) {
|
||||
const REDIS_CONFIG = Meteor.settings.private.redis;
|
||||
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
|
||||
const EVENT_NAME = 'PadCapturePubMsg';
|
||||
@ -12,7 +12,6 @@ export default function padCapture(meetingId, parentMeetingId, meetingName, sequ
|
||||
check(meetingId, String);
|
||||
check(parentMeetingId, String);
|
||||
check(meetingName, String);
|
||||
check(sequence, Number);
|
||||
|
||||
const pad = Pads.findOne(
|
||||
{
|
||||
@ -26,12 +25,12 @@ export default function padCapture(meetingId, parentMeetingId, meetingName, sequ
|
||||
},
|
||||
);
|
||||
|
||||
const filename = `${meetingName}-notes`;
|
||||
const payload = {
|
||||
parentMeetingId,
|
||||
breakoutId: meetingId,
|
||||
padId: pad.padId,
|
||||
meetingName,
|
||||
sequence,
|
||||
filename,
|
||||
};
|
||||
|
||||
Logger.info(`Sending PadCapturePubMsg for meetingId=${meetingId} parentMeetingId=${parentMeetingId} padId=${pad.padId}`);
|
||||
|
@ -5,6 +5,7 @@ import injectNotify from '/imports/ui/components/common/toast/inject-notify/comp
|
||||
import humanizeSeconds from '/imports/utils/humanizeSeconds';
|
||||
import _ from 'lodash';
|
||||
import BreakoutRemainingTimeComponent from './component';
|
||||
import BreakoutService from '/imports/ui/components/breakout-room/service';
|
||||
import { Text, Time } from './styles';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
@ -146,6 +147,7 @@ export default injectNotify(injectIntl(withTracker(({
|
||||
if (fromBreakoutPanel) data.bold = true;
|
||||
} else {
|
||||
clearInterval(timeRemainingInterval);
|
||||
BreakoutService.setCapturedNotesUploading();
|
||||
data.message = intl.formatMessage(timeEndedMessage || intlMessages.breakoutWillClose);
|
||||
}
|
||||
} else if (breakoutRoom) {
|
||||
|
@ -6,6 +6,8 @@ import Users from '/imports/api/users';
|
||||
import UserListService from '/imports/ui/components/user-list/service';
|
||||
import fp from 'lodash/fp';
|
||||
import UsersPersistentData from '/imports/api/users-persistent-data';
|
||||
import { UploadingPresentations } from '/imports/api/presentations';
|
||||
import _ from 'lodash';
|
||||
|
||||
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
|
||||
@ -36,7 +38,35 @@ const getBreakoutRoomUrl = (breakoutId) => {
|
||||
return breakoutUrlData;
|
||||
};
|
||||
|
||||
const setCapturedNotesUploading = () => {
|
||||
const breakoutRooms = findBreakouts();
|
||||
breakoutRooms.forEach((breakout) => {
|
||||
if (breakout.captureNotes) {
|
||||
const filename = breakout.shortName;
|
||||
const temporaryPresentationId = `${breakout.breakoutId}-notes`;
|
||||
|
||||
UploadingPresentations.upsert({
|
||||
temporaryPresentationId,
|
||||
}, {
|
||||
$set: {
|
||||
id: _.uniqueId(filename),
|
||||
temporaryPresentationId,
|
||||
progress: 0,
|
||||
filename,
|
||||
lastModifiedUploader: false,
|
||||
upload: {
|
||||
done: false,
|
||||
error: false,
|
||||
},
|
||||
uploadTimestamp: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const endAllBreakouts = () => {
|
||||
setCapturedNotesUploading();
|
||||
makeCall('endAllBreakouts');
|
||||
};
|
||||
|
||||
@ -214,4 +244,5 @@ export default {
|
||||
sortUsersByName: UserListService.sortUsersByName,
|
||||
isUserInBreakoutRoom,
|
||||
checkInviteModerators,
|
||||
setCapturedNotesUploading,
|
||||
};
|
||||
|
@ -539,7 +539,7 @@
|
||||
"windowMs": 90000,
|
||||
|
||||
// maximum number of requests per IP to allow during the rate limit window
|
||||
"max": 10
|
||||
"max": 16
|
||||
},
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user