Skip to content

Commit aedc541

Browse files
committed
Make narrative file name, type, and time-to-slide configurable in editor
1 parent 5b540ca commit aedc541

File tree

5 files changed

+117
-2
lines changed

5 files changed

+117
-2
lines changed

src/js/Storage.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ export class Storage {
315315
* @param {string} htmlFileName - The name of the presentation HTML file.
316316
*/
317317
async createNarratedHTMLFile(name, location, htmlFileName) {
318+
const t = this.presentation.narrativeType;
319+
if (!t || t === "none") {
320+
return;
321+
}
318322
try {
319323
const fileDescriptor = await this.backend.find(name, location);
320324
await this.backend.save(fileDescriptor, this.exportNarratedHTML(htmlFileName));

src/js/model/Presentation.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,25 @@ export class Presentation extends EventEmitter {
701701
* @type {boolean} */
702702
this.updateURLOnFrameChange = true;
703703

704+
/** The narrative file type or "none" if narration is disabled.
705+
*
706+
* @default
707+
* @type {string} */
708+
this.narrativeType = "none";
709+
710+
/** The narrative file name or relative path.
711+
*
712+
* @default
713+
* @type {string} */
714+
this.narrativeFile = "narrative.flac";
715+
716+
/** The narrative time-to-slide data mapping the time
717+
* moments (in seconds) to their corresponding frame number (one-based).
718+
*
719+
* @default
720+
* @type {string} */
721+
this.narrativeTimeToSlide = "0";
722+
704723
/** The last export document type.
705724
*
706725
* @default
@@ -846,6 +865,9 @@ export class Presentation extends EventEmitter {
846865
enableMouseRotation : this.enableMouseRotation,
847866
enableMouseNavigation : this.enableMouseNavigation,
848867
updateURLOnFrameChange : this.updateURLOnFrameChange,
868+
narrativeType : this.narrativeType,
869+
narrativeFile : this.narrativeFile,
870+
narrativeTimeToSlide : this.narrativeTimeToSlide,
849871
exportType : this.exportType,
850872
exportToPDFPageSize : this.exportToPDFPageSize,
851873
exportToPDFPageOrientation: this.exportToPDFPageOrientation,
@@ -902,6 +924,9 @@ export class Presentation extends EventEmitter {
902924
copyIfSet(this, storable, "enableMouseRotation");
903925
copyIfSet(this, storable, "enableMouseNavigation");
904926
copyIfSet(this, storable, "updateURLOnFrameChange");
927+
copyIfSet(this, storable, "narrativeType");
928+
copyIfSet(this, storable, "narrativeFile");
929+
copyIfSet(this, storable, "narrativeTimeToSlide");
905930
copyIfSet(this, storable, "exportType");
906931
copyIfSet(this, storable, "exportToPDFPageSize");
907932
copyIfSet(this, storable, "exportToPDFPageOrientation");

src/js/view/Properties.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export class Properties extends VirtualDOMView {
7474
render() {
7575
switch (this.mode) {
7676
case "preferences": return this.renderPreferences();
77+
case "narration": return this.renderNarrationProperties();
7778
case "export": return this.renderExportTool();
7879
default: return this.renderPresentationProperties();
7980
}
@@ -351,6 +352,86 @@ export class Properties extends VirtualDOMView {
351352
]);
352353
}
353354

355+
/** Render the properties view for a presentation-wide narrative.
356+
*
357+
* Three properties will be configured:
358+
* 1. narrativeType: the audio tag type; this field is a hint to
359+
* guide user in selection of an appropriate format, although
360+
* the browser can automatically detect the correct type by
361+
* itself. If this value is set to "none", narration will be
362+
* disabled. If it is set to flac or ogg, the corresponding
363+
* mimetype will be set for the <audio> type:
364+
* audio/flac or audio/ogg
365+
* 2. narrativeFile: the name (or relative path) of the narrative
366+
* file which must be available in side of the HTML file.
367+
* 3. narrativeTimeToSlide: the time-to-slide data value
368+
* Check the {narrativeTimeToSlideHelp} for details.
369+
*
370+
* @returns {VNode} - A virtual DOM tree.
371+
*/
372+
renderNarrationProperties() {
373+
const controller = this.controller;
374+
const _ = controller.gettext;
375+
const disabled = controller.presentation.narrativeType === "none";
376+
const toDefaultMode = () => this.toggleMode("default");
377+
378+
return h("div.properties", [
379+
h("div.back", {
380+
title: _("Back to presentation properties"),
381+
onClick() { toDefaultMode(); }
382+
}, h("i.fas.fa-arrow-left", )),
383+
384+
h("h1", _("Narration")),
385+
386+
h("label", {for: "field-narrativeType"}, _("Narrative file type")),
387+
this.renderSelectField("narrativeType", controller.getPresentationProperty, controller.setPresentationProperty, {
388+
none: _("None (disable narration)"),
389+
flac: _("Free Lossless Audio Codec (FLAC)"),
390+
ogg: _("Ogg (a container for Vorbis or Opus)"),
391+
}),
392+
393+
h("label.side-by-side", {for: "field-narrativeFile"}, [
394+
_("Narrative file name"),
395+
this.renderHelp(_("Name or path of the narrative file"),
396+
() => controller.info(
397+
_("Name or path of the narrative file (relative to the HTML file location)"),
398+
true
399+
)
400+
)
401+
]),
402+
this.renderTextField("narrativeFile", disabled, controller.getPresentationProperty, controller.setPresentationProperty, false),
403+
404+
h("label.side-by-side", {for: "field-narrativeTimeToSlide"}, [
405+
_("Time to frame number mapping"),
406+
this.renderHelp(_("Click here to see the syntax for this field"), () => controller.info(this.narrativeTimeToSlideHelp, true))
407+
]),
408+
this.renderTextField("narrativeTimeToSlide", disabled, controller.getPresentationProperty, controller.setPresentationProperty, false)
409+
]);
410+
}
411+
412+
/** The HTML of the help message for time-to-slide data.
413+
*
414+
* @readonly
415+
* @type {string}
416+
*/
417+
get narrativeTimeToSlideHelp() {
418+
const _ = this.controller.gettext;
419+
return [
420+
_("Format of the narrative time-to-slide data:"),
421+
"<ul><li>" + [
422+
_("A string of comma-separated descriptors"),
423+
_("Each descriptor specifies the time instant (in seconds) which should trigger a slide transition"),
424+
_("Descriptor can specify the target slide number (counting from one); for this purpose the time and slide index must be separated by a colon"),
425+
].join("<li>") + "</ul>",
426+
_("Examples:"),
427+
"<ul><li>" + [
428+
_("\"0\": Switch to the first slide when narrative is at zero second without any other slide transition"),
429+
_("\"0,5,7\": Switch to the 1st, 2nd, and 3rd slides at 0, 5, and 7 seconds of narrative"),
430+
_("\"0,5,7:1,10:4\": At time 7s, return to the 1st slide and at 10s resume to the 4th slide"),
431+
].join("<li>") + "</ul>",
432+
].join("<br>");
433+
}
434+
354435
/** The HTML of the help message for include/exclude lists in export.
355436
*
356437
* @readonly

src/js/view/Toolbar.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ export class Toolbar extends VirtualDOMView {
141141
title: _("Reload the SVG document"),
142142
onclick() { controller.reload(); }
143143
}, h("i.fas.fa-sync")),
144+
h("button", {
145+
title: _("Narrate the presentation"),
146+
className: properties.mode === "narration" ? "active" : undefined,
147+
onclick() { properties.toggleMode("narration"); }
148+
}, h("i.fas.fa-voicemail")), // alternatives are file-audio, microphone-alt, and volume-up
144149
// TODO disable the Export button if the feature is not available
145150
h("button", {
146151
title: _("Export the presentation"),

src/templates/narrated.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
<iframe class="presentation-container" src="{{ soziHtml }}">
3838
The Sozi presentation should play here.
3939
</iframe>
40-
<audio id="narrative" controls data-time-to-slide="0:1,1:3,2:6">
41-
<source src="narrative.flac" type="audio/flac" preload="auto">
40+
<audio id="narrative" controls data-time-to-slide="{{ pres.narrativeTimeToSlide }}">
41+
<source src="{{ pres.narrativeFile }}" type="audio/{{ pres.narrativeType }}" preload="auto">
4242
Audio HTML5 tag is not supported!
4343
</audio>
4444
{% endraw %}

0 commit comments

Comments
 (0)