aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlonkaars <loek@pipeframe.xyz>2021-07-24 15:27:56 +0200
committerlonkaars <loek@pipeframe.xyz>2021-07-24 15:27:56 +0200
commitff0609458ab50dd627b13b604f7c8c42af9a67b3 (patch)
treeaa7baede33ba3f9bc083e1762d27009787a7c0b1
parent6f800c0ba26c3936c626c81c6d9a5592cfa2b26f (diff)
video source settings beginnings
-rw-r--r--components/icons.tsx12
-rw-r--r--components/videosourcesettings.tsx43
-rw-r--r--pages/editor.tsx97
-rw-r--r--project.ts39
4 files changed, 117 insertions, 74 deletions
diff --git a/components/icons.tsx b/components/icons.tsx
index 5e06bf8..3240f9e 100644
--- a/components/icons.tsx
+++ b/components/icons.tsx
@@ -267,7 +267,7 @@ export function BracketsRoundedIcon() {
</svg>;
}
-export function SlashIconRounded() {
+export function SlashRoundedIcon() {
return <svg width='24' height='24' viewBox='0 0 24 24' fill='currentColor' xmlns='http://www.w3.org/2000/svg'>
<path
fill-rule='evenodd'
@@ -276,3 +276,13 @@ export function SlashIconRounded() {
/>
</svg>;
}
+
+export function UploadRoundedIcon() {
+ return <svg width='24' height='24' viewBox='0 0 24 24' fill='currentColor' xmlns='http://www.w3.org/2000/svg'>
+ <path
+ d='M6.70711 8.29289L11.2929 3.70711C11.6834 3.31658 12.3166 3.31658 12.7071 3.70711L17.2929 8.29289C17.9229 8.92286 17.4767 10 16.5858 10H7.41421C6.52331 10 6.07714 8.92286 6.70711 8.29289Z'
+ />
+ <path d='M9 10H15V15C15 15.5523 14.5523 16 14 16H10C9.44772 16 9 15.5523 9 15V10Z' />
+ <rect x='5' y='18' width='14' height='2' rx='1' />
+ </svg>;
+}
diff --git a/components/videosourcesettings.tsx b/components/videosourcesettings.tsx
new file mode 100644
index 0000000..ae7f75b
--- /dev/null
+++ b/components/videosourcesettings.tsx
@@ -0,0 +1,43 @@
+import { useRef } from 'react';
+import { LocalVideo } from '../project';
+
+import Button from '@material-ui/core/Button';
+import Switch from '@material-ui/core/Switch';
+
+import { UploadRoundedIcon } from './icons';
+
+export function LocalVideoSettings(props: { settings: LocalVideo; }) {
+ var fileUploadRef = useRef(null);
+
+ return <>
+ <input
+ ref={fileUploadRef}
+ type='file'
+ accept='video/*'
+ className='dispnone'
+ onChange={event => {
+ var file = event.target.files[0];
+ if (!file) return;
+ var reader = new FileReader();
+ reader.addEventListener('load', async ev => {
+ props.settings.load(ev.target.result as ArrayBuffer);
+ });
+ reader.readAsArrayBuffer(file);
+ }}
+ />
+ <Button
+ variant='contained'
+ color='default'
+ children='Load video'
+ onClick={() => (fileUploadRef.current as HTMLInputElement).click()}
+ startIcon={<UploadRoundedIcon />}
+ />
+ <div className='sidebyside'>
+ <span className='body'>Fully buffer video before presentation</span>
+ <Switch
+ value={props.settings.config.fullyBuffer}
+ onChange={() => props.settings.config.fullyBuffer = !props.settings.config.fullyBuffer}
+ />
+ </div>
+ </>;
+}
diff --git a/pages/editor.tsx b/pages/editor.tsx
index fac8899..5a63cb4 100644
--- a/pages/editor.tsx
+++ b/pages/editor.tsx
@@ -8,18 +8,17 @@ import { v4 as uuid } from 'uuid';
import FadeThroughTransition from '../components/fadethrough';
import {
- BracketsRoundedIcon,
FullScreenControlsRoundedIcon,
MenuBarControlsRoundedIcon,
PressureIcon,
- SlashIconRounded,
+ SlashRoundedIcon,
SlideKeyframe,
} from '../components/icons';
import KeybindSelector from '../components/keybindselector';
import PlaySkipIconAni from '../components/play-skip';
import Selection from '../components/selection';
import TimecodeInput from '../components/timeinput';
-import Project, { LocalVideo } from '../project';
+import Project, { LocalVideo, VideoSources, VideoSourceType } from '../project';
import timeline, {
anySlide,
clickThroughBehaviours,
@@ -34,11 +33,8 @@ import { TimedVideoPlayer } from './present';
import AppBar from '@material-ui/core/AppBar';
import Button from '@material-ui/core/Button';
-import Dialog from '@material-ui/core/Dialog';
-import MuiDialogTitle from '@material-ui/core/DialogTitle';
import Fab from '@material-ui/core/Fab';
import FormControl from '@material-ui/core/FormControl';
-import IconButton from '@material-ui/core/IconButton';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import Select from '@material-ui/core/Select';
@@ -54,18 +50,13 @@ import Icon from '@mdi/react';
import AddRoundedIcon from '@material-ui/icons/AddRounded';
import ArrowDropDownRoundedIcon from '@material-ui/icons/ArrowDropDownRounded';
-import CloseIcon from '@material-ui/icons/Close';
import FullscreenRoundedIcon from '@material-ui/icons/FullscreenRounded';
-import KeyboardArrowDownRoundedIcon from '@material-ui/icons/KeyboardArrowDownRounded';
import NavigateBeforeRoundedIcon from '@material-ui/icons/NavigateBeforeRounded';
import NavigateNextRoundedIcon from '@material-ui/icons/NavigateNextRounded';
-import SettingsRoundedIcon from '@material-ui/icons/SettingsRounded';
import SkipPreviousRoundedIcon from '@material-ui/icons/SkipPreviousRounded';
import { mdiCursorDefault } from '@mdi/js';
import DescriptionRoundedIcon from '@material-ui/icons/DescriptionRounded';
-import GetAppRoundedIcon from '@material-ui/icons/GetAppRounded';
-import VideoLabelRoundedIcon from '@material-ui/icons/VideoLabelRounded';
var keyframeInAnimations: { [key: string]: { x: number; y: number; }; } = {};
var slideAPIs: { [key: string]: any; }[] = [];
@@ -92,9 +83,6 @@ interface globalState {
right: slideTypes | null;
};
};
- dialog: {
- projectSettings: boolean;
- };
settings: {
node: ReactNode;
name: string;
@@ -130,9 +118,6 @@ var global = createState<globalState>({
right: null,
},
},
- dialog: {
- projectSettings: false,
- },
ready: {
timeline: false,
video: {
@@ -981,50 +966,17 @@ function TimelineEditor() {
</div>;
}
-function DialogBox(props: {
- open?: boolean;
- close: () => any;
- children: ReactNode;
- title: string;
-}) {
- return <Dialog open={props.open} onClose={props.close}>
- <MuiDialogTitle>
- <span className='title'>{props.title}</span>
- <IconButton onClick={props.close} children={<CloseIcon />} />
- </MuiDialogTitle>
- <div className='inner'>
- {props.children}
- </div>
- </Dialog>;
-}
-
-function ProjectSettings() {
- var proj = useHookstate(project).timeline;
- var open = useHookstate(global).dialog.projectSettings;
-
- function close() {
- global.update.refreshLiveTimeline.value();
- open.set(false);
- }
-
- return <DialogBox open={open.get()} close={close} title='Project settings'>
- <div className='body fullwidth-inputs form-spacing'>
- <span>This is being worked on :tada:</span>
- </div>
- </DialogBox>;
-}
-
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;
-
return <>
- <ProjectSettings />
<h2 className='title posabs h0 t0'>Presentation settings</h2>
<div className='scroll posabs h0 b0'>
<div className={'section ' + (ready.timeline.value ? '' : 'disabled')}>
@@ -1094,6 +1046,34 @@ function DefaultSettings() {
/>
<KeybindSelector label='Show menu' value={showMenuKeybinds} onChange={setShowMenuKeybinds} />
</div>
+ <div className={'section ' + (ready.timeline.value ? '' : 'disabled')}>
+ <span className='title'>Source</span>
+ <FormControl variant='filled'>
+ <InputLabel>Video source</InputLabel>
+ <Select
+ value={projectFile.video?.type || ''}
+ onChange={e =>
+ projectFile.video = new (VideoSources.find(s =>
+ s.type == e.target.value as VideoSourceType
+ ).class)()}
+ IconComponent={ArrowDropDownRoundedIcon}
+ >
+ {VideoSources.map(s => <MenuItem value={s.type} children={s.name} />)}
+ </Select>
+ </FormControl>
+ {(() => {
+ if (!projectFile.video) return null;
+ var SourceSettings = VideoSources.find(s => s.type == projectFile.video.type).settings;
+ return <SourceSettings settings={projectFile.video} />;
+ })()}
+ </div>
+ <div className={'section ' + (ready.timeline.value ? '' : 'disabled')}>
+ <span className='title'>Remotes</span>
+ <div className='sidebyside'>
+ <span className='body'>Allow anonymous remotes</span>
+ <Switch />
+ </div>
+ </div>
<div className='section'>
<span className='title'>Cool temporary buttons</span>
<input
@@ -1153,7 +1133,7 @@ function DefaultSettings() {
projectFile.downloadProjectFile();
}}
/>
- <Button
+ {false && <Button
variant='contained'
color='default'
children='New project'
@@ -1170,10 +1150,9 @@ function DefaultSettings() {
global.timeline.workingTimeline.set(player.timeline.slides);
global.update.refreshLiveTimeline.value();
global.ready.timeline.set(true);
- global.dialog.projectSettings.set(true);
}}
startIcon={<AddRoundedIcon />}
- />
+ />}
</div>
</div>
</>;
@@ -1451,7 +1430,7 @@ function TitleBar() {
<h1>pressure</h1>
<div className={'posabs abscenter projarea ' + (ready.timeline.get() ? '' : 'disabled')}>
<span className='projfolder'>My presentations</span>
- <SlashIconRounded />
+ <SlashRoundedIcon />
<span
className='projname'
contentEditable
@@ -1460,12 +1439,6 @@ function TitleBar() {
onBlur={() => proj.name.set((nameRef.current as HTMLSpanElement).textContent.trim())}
children={proj.name.get()}
/>
- <KeyboardArrowDownRoundedIcon
- className='cpointer'
- onClick={() => {
- global.dialog.projectSettings.set(true);
- }}
- />
</div>
</Toolbar>
</AppBar>;
diff --git a/project.ts b/project.ts
index 1c7cbf1..6bf561a 100644
--- a/project.ts
+++ b/project.ts
@@ -2,12 +2,19 @@ import { MediaInfo as Mediainfo, ResultObject } from 'mediainfo.js/dist/types';
import JSZip from 'jszip';
import { TimedVideoPlayer } from './pages/present';
+import { LocalVideoSettings } from './components/videosourcesettings';
+
// garbage garbage garbage
declare var MediaInfo: () => Promise<Mediainfo>;
const filext = '.prspr';
-export class LocalVideo {
+interface VideoSource {
+ load: (data: ArrayBuffer) => void;
+ save: (dir: JSZip) => void;
+}
+
+export class LocalVideo implements VideoSource {
source: ArrayBuffer;
type: string;
mimetype: string;
@@ -15,8 +22,15 @@ export class LocalVideo {
framerate: number;
framecount: number;
+ config: {
+ fullyBuffer: boolean;
+ };
+
constructor(video?: ArrayBuffer) {
this.type = 'local';
+ this.config = {
+ fullyBuffer: false,
+ };
if (video) this.load(video);
}
@@ -45,21 +59,21 @@ export class LocalVideo {
}
}
-export const VideoSourceTypeToClass = {
- 'local': LocalVideo,
-} as const;
-// export type VideoSources = [LocalVideo];
-export type valueof<T> = T[keyof T];
-export type VideoSources = InstanceType<valueof<typeof VideoSourceTypeToClass>>;
+export const VideoSources = [
+ { type: 'local', class: LocalVideo, name: 'Local video', settings: LocalVideoSettings },
+] as const;
+
+export type VideoSourceClass = InstanceType<typeof VideoSources[number]['class']>;
+export type VideoSourceType = typeof VideoSources[number]['type'];
export default class {
version: string;
zip: JSZip;
project: TimedVideoPlayer['timeline'];
- video: VideoSources;
+ video: VideoSourceClass;
constructor() {
- this.version = '0.1.0';
+ this.version = '0.1.1';
this.zip = new JSZip();
}
@@ -89,6 +103,7 @@ export default class {
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));
}
@@ -104,8 +119,10 @@ export default class {
framecount: Number(await this.zip.file('source/framecount').async('string')),
} as TimedVideoPlayer['timeline'];
var type = await this.zip.file('source/type').async('string');
- if (!VideoSourceTypeToClass.hasOwnProperty(type)) return;
- this.video = new VideoSourceTypeToClass[type](await this.zip.file('source/video').async('arraybuffer'));
+ var videoSourceType = VideoSources.find(s => s.type == type);
+ if (!videoSourceType) return;
+ this.video = new videoSourceType.class(await this.zip.file('source/video').async('arraybuffer'));
this.video.mimetype = await this.zip.file('source/mimetype').async('string');
+ this.video.config = JSON.parse(await this.zip.file('source/config').async('string'));
}
}