aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json4
-rw-r--r--pages/editor.tsx67
-rw-r--r--pages/present.tsx17
-rw-r--r--project.ts51
-rw-r--r--timeline.schema.json210
-rw-r--r--timeline.ts5
6 files changed, 91 insertions, 263 deletions
diff --git a/package.json b/package.json
index 9637f4d..b5f16d5 100644
--- a/package.json
+++ b/package.json
@@ -10,8 +10,7 @@
"scripts": {
"dev": "next dev",
"build": "next build",
- "start": "next start",
- "schema": "npx ts-json-schema-generator --path timeline.ts --type timeline > timeline.schema.json"
+ "start": "next start"
},
"dependencies": {
"@hookstate/core": "^3.0.8",
@@ -36,7 +35,6 @@
"react-use-gesture": "^9.1.3",
"semver": "^7.3.5",
"timecode-boss": "^4.2.3",
- "ts-json-schema-generator": "^0.93.0",
"use-mousetrap": "^1.0.4",
"uuid": "^8.3.2"
},
diff --git a/pages/editor.tsx b/pages/editor.tsx
index b90ba7f..f9c0e80 100644
--- a/pages/editor.tsx
+++ b/pages/editor.tsx
@@ -18,13 +18,12 @@ import KeybindSelector from '../components/keybindselector';
import PlaySkipIconAni from '../components/play-skip';
import Selection from '../components/selection';
import TimecodeInput from '../components/timeinput';
-import Project, { arrayBufferToBase64, VideoSources, VideoSourceType } from '../project';
+import Project, { arrayBufferToBase64, PresentationSettings, VideoSources, VideoSourceType } from '../project';
import timeline, {
anySlide,
clickThroughBehaviours,
loopBeginSlide,
loopSlide,
- presentationSettings,
slide,
slideTypes,
toolToSlide,
@@ -155,9 +154,6 @@ var project = createState<project>({
timeline: {
name: '',
slides: [],
- settings: {
- controlType: 'FullScreen',
- },
framerate: 0,
framecount: 0,
},
@@ -967,15 +963,10 @@ function TimelineEditor() {
}
function DefaultSettings() {
- var [nextSlideKeybinds, setNextSlideKeybinds] = useState(['Space', 'n', 'Enter']);
- var [previousSlideKeybinds, setPreviousSlideKeybinds] = useState(['Backspace', 'p']);
- var [showMenuKeybinds, setShowMenuKeybinds] = useState(['Escape', 'm']);
-
- var [videoSourceType, setVideoSourceType] = useState(VideoSources[0].type);
-
- var proj = useHookstate(project).timeline;
-
var ready = useHookstate(global).ready;
+ var [dummy, setDummy] = useState(false);
+ var rerender = () => setDummy(!dummy);
+
return <>
<h2 className='title posabs h0 t0'>Presentation settings</h2>
<div className='scroll posabs h0 b0'>
@@ -983,14 +974,23 @@ function DefaultSettings() {
<span className='title'>Controls</span>
<div className='sidebyside'>
<span className='body'>Allow remote control during presentation</span>
- <Switch />
+ <Switch
+ value={projectFile.settings.remotes.AllowRemotes}
+ onChange={() => {
+ projectFile.settings.remotes.AllowRemotes = !projectFile.settings.remotes.AllowRemotes;
+ rerender();
+ }}
+ />
</div>
<FormControl variant='filled'>
<InputLabel>On-screen controls</InputLabel>
<Select
- value={proj.settings.controlType.get()}
- onChange={e =>
- proj.settings.controlType.set(e.target.value as presentationSettings['controlType'])}
+ value={projectFile.settings.controls.ControlType}
+ onChange={e => {
+ projectFile.settings.controls.ControlType = e.target
+ .value as PresentationSettings['controls']['ControlType'];
+ rerender();
+ }}
IconComponent={ArrowDropDownRoundedIcon}
>
<MenuItem value='FullScreen'>
@@ -1038,13 +1038,30 @@ function DefaultSettings() {
</div>
<div className={'section ' + (ready.timeline.value ? '' : 'disabled')}>
<span className='title'>Keybindings</span>
- <KeybindSelector label='Next slide' value={nextSlideKeybinds} onChange={setNextSlideKeybinds} />
+ <KeybindSelector
+ label='Next slide'
+ value={projectFile.settings.keybindings.NextSlide}
+ onChange={e => {
+ projectFile.settings.keybindings.NextSlide = e;
+ rerender();
+ }}
+ />
<KeybindSelector
label='Previous slide'
- value={previousSlideKeybinds}
- onChange={setPreviousSlideKeybinds}
+ value={projectFile.settings.keybindings.PreviousSlide}
+ onChange={e => {
+ projectFile.settings.keybindings.PreviousSlide = e;
+ rerender();
+ }}
+ />
+ <KeybindSelector
+ label='Show menu'
+ value={projectFile.settings.keybindings.ShowMenu}
+ onChange={e => {
+ projectFile.settings.keybindings.ShowMenu = e;
+ rerender();
+ }}
/>
- <KeybindSelector label='Show menu' value={showMenuKeybinds} onChange={setShowMenuKeybinds} />
</div>
<div className={'section ' + (ready.timeline.value ? '' : 'disabled')}>
<span className='title'>Source</span>
@@ -1071,7 +1088,13 @@ function DefaultSettings() {
<span className='title'>Remotes</span>
<div className='sidebyside'>
<span className='body'>Allow anonymous remotes</span>
- <Switch />
+ <Switch
+ value={projectFile.settings.remotes.AllowQRRemotes}
+ onChange={() => {
+ projectFile.settings.remotes.AllowQRRemotes = !projectFile.settings.remotes.AllowQRRemotes;
+ rerender();
+ }}
+ />
</div>
</div>
<div className='section'>
diff --git a/pages/present.tsx b/pages/present.tsx
index fe46a5f..a99d239 100644
--- a/pages/present.tsx
+++ b/pages/present.tsx
@@ -152,20 +152,7 @@ export class TimedVideoPlayer {
}
loadSlides(jsonString: string) {
- try {
- var timeline = JSON.parse(jsonString);
- } catch (e) {
- console.log('invalid json object!' + e);
- return;
- }
- var ajv = new Ajv({ allErrors: true });
- var validate = ajv.compile(timelineSchema);
- if (!validate(timeline)) {
- console.log('schema not passed!');
- return;
- }
-
- this.timeline = timeline as timeline;
+ this.timeline = JSON.parse(jsonString) as timeline;
this.timeline.slides[-1] = {
id: '00000000-0000-0000-0000-000000000000',
@@ -214,7 +201,7 @@ export class TimedVideoPlayer {
export default function Present() {
var [dummy, setDummy] = useState(false);
var rerender = () => setDummy(!dummy);
- var [player, setPlayer] = useState(new TimedVideoPlayer());
+ var [player, _setPlayer] = useState(new TimedVideoPlayer());
useEffect(() => {
setInterval(() => {
diff --git a/project.ts b/project.ts
index 6ddd29b..be62416 100644
--- a/project.ts
+++ b/project.ts
@@ -74,14 +74,46 @@ export const VideoSources = [
export type VideoSourceClass = InstanceType<typeof VideoSources[number]['class']>;
export type VideoSourceType = typeof VideoSources[number]['type'];
+export class PresentationSettings {
+ keybindings: {
+ NextSlide: string[];
+ PreviousSlide: string[];
+ ShowMenu: string[];
+ };
+ controls: {
+ ControlType: 'FullScreen' | 'MenuBar';
+ };
+ remotes: {
+ AllowRemotes: boolean;
+ AllowQRRemotes: boolean;
+ };
+
+ constructor() {
+ this.keybindings = {
+ NextSlide: ['Space', 'n', 'Enter'],
+ PreviousSlide: ['Backspace', 'p'],
+ ShowMenu: ['Escape', 'm'],
+ };
+ this.controls = {
+ ControlType: 'FullScreen',
+ };
+ this.remotes = {
+ AllowRemotes: false,
+ AllowQRRemotes: false,
+ };
+ }
+}
+
export default class {
- version: string;
+ version: string = '0.2.0';
+ fileVersion: string;
zip: JSZip;
project: TimedVideoPlayer['timeline'];
video: VideoSourceClass;
+ settings: PresentationSettings;
constructor() {
- this.version = '0.1.1';
+ this.settings = new PresentationSettings();
this.zip = new JSZip();
}
@@ -101,29 +133,28 @@ export default class {
}
saveProject() {
+ this.zip = new JSZip();
+
var meta = this.zip.folder('meta');
meta.file('version', this.version);
meta.file('name', this.project.name);
- var settings = this.zip.folder('settings');
- settings.file('controlType', this.project.settings.controlType);
-
var source = this.zip.folder('source');
this.video.save(source);
source.file('mimetype', this.video.mimetype);
source.file('config', JSON.stringify(this.video.config, null, 4));
this.zip.file('slides', JSON.stringify(this.project.slides, null, 4));
+ this.zip.file('settings', JSON.stringify(this.settings, null, 4));
}
async openProject(data: ArrayBuffer) {
this.zip = new JSZip();
await this.zip.loadAsync(data);
- this.version = await this.zip.file('meta/version').async('string');
+ this.fileVersion = await this.zip.file('meta/version').async('string');
this.project = {
name: await this.zip.file('meta/name').async('string'),
- settings: { controlType: await this.zip.file('settings/controlType').async('string') },
slides: JSON.parse(await this.zip.file('slides').async('string')),
framerate: Number(await this.zip.file('source/framerate').async('string')),
framecount: Number(await this.zip.file('source/framecount').async('string')),
@@ -135,8 +166,12 @@ export default class {
this.video = new videoSourceType.class(await this.zip.file('source/video').async('arraybuffer'));
this.video.mimetype = await this.zip.file('source/mimetype').async('string');
- if (semver.lt('0.1.1', this.version)) {
+ if (semver.lt('0.1.1', this.fileVersion)) {
this.video.config = JSON.parse(await this.zip.file('source/config').async('string'));
}
+
+ if (semver.lt('0.2.0', this.fileVersion)) {
+ this.settings = JSON.parse(await this.zip.file('settings').async('string'));
+ }
}
}
diff --git a/timeline.schema.json b/timeline.schema.json
deleted file mode 100644
index 3076125..0000000
--- a/timeline.schema.json
+++ /dev/null
@@ -1,210 +0,0 @@
-{
- "$ref": "#/definitions/timeline",
- "$schema": "http://json-schema.org/draft-07/schema#",
- "definitions": {
- "anySlide": {
- "anyOf": [
- {
- "$ref": "#/definitions/slide"
- },
- {
- "$ref": "#/definitions/delaySlide"
- },
- {
- "$ref": "#/definitions/speedChangeSlide"
- },
- {
- "$ref": "#/definitions/loopSlide"
- }
- ]
- },
- "delaySlide": {
- "additionalProperties": false,
- "properties": {
- "clickThroughBehaviour": {
- "enum": [
- "ImmediatelySkip",
- "PlayOut"
- ],
- "type": "string"
- },
- "delay": {
- "type": "number"
- },
- "frame": {
- "type": "number"
- },
- "id": {
- "type": "string"
- },
- "type": {
- "$ref": "#/definitions/slideTypes"
- }
- },
- "required": [
- "clickThroughBehaviour",
- "delay",
- "frame",
- "id",
- "type"
- ],
- "type": "object"
- },
- "loopSlide": {
- "additionalProperties": false,
- "properties": {
- "beginFrame": {
- "type": "number"
- },
- "clickThroughBehaviour": {
- "enum": [
- "ImmediatelySkip",
- "PlayOut"
- ],
- "type": "string"
- },
- "frame": {
- "type": "number"
- },
- "id": {
- "type": "string"
- },
- "playbackType": {
- "enum": [
- "PingPong",
- "Normal"
- ],
- "type": "string"
- },
- "type": {
- "$ref": "#/definitions/slideTypes"
- }
- },
- "required": [
- "beginFrame",
- "clickThroughBehaviour",
- "frame",
- "id",
- "playbackType",
- "type"
- ],
- "type": "object"
- },
- "presentationSettings": {
- "additionalProperties": false,
- "properties": {
- "controlType": {
- "enum": [
- "FullScreen",
- "MenuBar"
- ],
- "type": "string"
- }
- },
- "required": [
- "controlType"
- ],
- "type": "object"
- },
- "slide": {
- "additionalProperties": false,
- "properties": {
- "clickThroughBehaviour": {
- "enum": [
- "ImmediatelySkip",
- "PlayOut"
- ],
- "type": "string"
- },
- "frame": {
- "type": "number"
- },
- "id": {
- "type": "string"
- },
- "type": {
- "$ref": "#/definitions/slideTypes"
- }
- },
- "required": [
- "clickThroughBehaviour",
- "type",
- "id",
- "frame"
- ],
- "type": "object"
- },
- "slideTypes": {
- "enum": [
- "default",
- "delay",
- "speedChange",
- "loop"
- ],
- "type": "string"
- },
- "speedChangeSlide": {
- "additionalProperties": false,
- "properties": {
- "clickThroughBehaviour": {
- "enum": [
- "ImmediatelySkip",
- "PlayOut"
- ],
- "type": "string"
- },
- "frame": {
- "type": "number"
- },
- "id": {
- "type": "string"
- },
- "newFramerate": {
- "type": "number"
- },
- "type": {
- "$ref": "#/definitions/slideTypes"
- }
- },
- "required": [
- "clickThroughBehaviour",
- "frame",
- "id",
- "newFramerate",
- "type"
- ],
- "type": "object"
- },
- "timeline": {
- "additionalProperties": false,
- "properties": {
- "framecount": {
- "type": "number"
- },
- "framerate": {
- "type": "number"
- },
- "name": {
- "type": "string"
- },
- "settings": {
- "$ref": "#/definitions/presentationSettings"
- },
- "slides": {
- "items": {
- "$ref": "#/definitions/anySlide"
- },
- "type": "array"
- }
- },
- "required": [
- "slides",
- "framecount",
- "framerate",
- "name",
- "settings"
- ],
- "type": "object"
- }
- }
-}
diff --git a/timeline.ts b/timeline.ts
index d0780ff..052ae8d 100644
--- a/timeline.ts
+++ b/timeline.ts
@@ -50,14 +50,9 @@ export var toolToSlide = {
loop: loopSlide,
};
-export interface presentationSettings {
- controlType: 'FullScreen' | 'MenuBar';
-}
-
export default interface timeline {
slides: Array<anySlide>;
framecount: number;
framerate: number;
name: string;
- settings: presentationSettings;
}