Skip to content
Snippets Groups Projects
Commit bcde2f79 authored by Nicolas Pope's avatar Nicolas Pope
Browse files

Merge branch 'feature/device-check' into 'master'

Check device exists and thumb per frame

See merge request nicolas.pope/ftl-web!8
parents 64c35385 06399803
No related branches found
No related tags found
1 merge request!8Check device exists and thumb per frame
......@@ -4,6 +4,7 @@ import axios from 'axios';
interface IFrame {
title?: string;
nodeId?: string;
deviceId?: string;
active?: boolean;
frameId: number;
autoStart?: boolean;
......
......@@ -2,7 +2,7 @@ import { Form as FormikForm } from 'formik';
import styled from 'styled-components';
export const Form = styled(FormikForm)`
input[type=text], input[type=number] {
input[type=text], input[type=number], input[type=password] {
border: 2px solid ${props => props.theme.border.green};
border-radius: 5px;
box-sizing: border-box;
......@@ -24,6 +24,28 @@ export const Form = styled(FormikForm)`
}
}
select {
border: 2px solid ${props => props.theme.border.green};
border-radius: 5px;
box-sizing: border-box;
padding: 0.5rem;
font-family: 'Open Sans',Helvetica,Sans-Serif;
outline: none;
background: white;
&:focus {
border: 2px solid ${props => props.theme.border.purple};
}
&:active {
border: 2px solid ${props => props.theme.border.purple};
}
&:disabled {
border: 2px solid ${props => props.theme.border.disabled};
}
}
fieldset {
display: grid;
grid-template-columns: auto 1fr;
......
......@@ -7,7 +7,7 @@ export function buildCards(streams: IStream[], nodes: INode[]): ICardDetails[] {
const cStream: ICardDetails[] = streams.reduce((r, s) => [...r, ...s.framesets.reduce((rr, fs) => [...rr, ...fs.frames.map(f => ({
type: 'stream',
title: f.title || fs.title || s.title || s.uri,
image: `${path}v1/streams/${s.id}/thumbnail`,
image: `${path}v1/streams/${s.id}/thumbnail/${fs.framesetId}/${f.frameId}`,
status: f.active ? 'active' : 'offline',
link: `${path}view?s=${encodeURIComponent(s.uri)}&id=${s.id}&fsid=${fs.framesetId}&fid=${f.frameId}`,
editable: true,
......
import React, { useState, useEffect } from 'react';
import { Formik, Field, FieldArray } from 'formik';
import { Formik, Field } from 'formik';
import { useParams } from 'react-router';
import { useHistory } from 'react-router-dom';
import { Form, ButtonBar } from '../../components/Form';
import { Container } from './styledComponents';
import { TableContainer } from '../Node/styledComponents';
import { getStream, saveStream, IStream } from '../../api/streams';
import { INode } from '../../api/nodes';
import { FaCircle, FaPen } from 'react-icons/fa';
import { getConfigs, IConfig, createConfig } from '../../api/configs';
import { Table } from '../../components/Table';
......@@ -13,8 +14,15 @@ import {IconButton} from '../../components/IconButton';
import {ConfigDialog} from './ConfigDialog';
import {NewConfigDialog} from './NewConfigDialog';
import { TitleRow } from '../Streams/styledComponents';
import { useRecoilValue } from 'recoil';
import { nodeList } from '../../recoil/atoms';
function Frame({values, fsix, fix}: {values: any, fsix: number, fix: number}) {
const nodes = useRecoilValue<INode[]>(nodeList) || [];
const nodeId = values.framesets[fsix].frames[fix].nodeId;
const node = nodeId ? nodes.find(n => n.serial === nodeId) : null;
const devices = node ? node.devices : [];
return <>
<label htmlFor={`framesets.${fsix}.framesetId`}>Set ID</label>
<Field type="text" name={`framesets.${fsix}.framesetId`} disabled={fix > 0}/>
......@@ -25,7 +33,15 @@ function Frame({values, fsix, fix}: {values: any, fsix: number, fix: number}) {
<label htmlFor={`framesets.${fsix}.frames.${fix}.title`}>Frame Title</label>
<Field type="text" name={`framesets.${fsix}.frames.${fix}.title`} />
<label htmlFor={`framesets.${fsix}.frames.${fix}.nodeId`}>Node</label>
<Field type="text" name={`framesets.${fsix}.frames.${fix}.nodeId`} />
<Field as="select" name={`framesets.${fsix}.frames.${fix}.nodeId`}>
<option key="default" value={'none'}>None</option>
{nodes.map((n, ix) => <option key={ix} value={n.serial}>{n.name || n._id}</option>)}
</Field>
<label htmlFor={`framesets.${fsix}.frames.${fix}.deviceId`}>Device</label>
<Field as="select" name={`framesets.${fsix}.frames.${fix}.deviceId`} disabled={devices.length === 0}>
<option key={-1} value={undefined}>None</option>
{devices.map((n, ix) => <option key={ix} value={n.serial}>{`${n.name} (${n.serial})`}</option>)}
</Field>
<label htmlFor={`framesets.${fsix}.frames.${fix}.autoStart`}>Auto-start</label>
<Field type="checkbox" name={`framesets.${fsix}.frames.${fix}.autoStart`} />
</>;
......
......@@ -39,12 +39,12 @@ export default class Streams {
return result;
}
@Get('/:id/thumbnail')
@Get('/:id/thumbnail/:fs/:f')
@Header({
'Content-Type': 'image/jpeg',
})
async getThumbnail(@PathParams('id') id: string, @UseToken() token: AccessToken): Promise<Buffer> {
const result = await this.streamService.getThumbnail(id, token.groups);
async getThumbnail(@PathParams('id') id: string, @PathParams('fs') frameset: number, @PathParams('f') frame: number, @UseToken() token: AccessToken): Promise<Buffer> {
const result = await this.streamService.getThumbnail(id, frameset, frame, token.groups);
if (!result) {
return defaultThumb;
}
......
......@@ -28,6 +28,7 @@ function framesByNodeId(streams: MongooseDocument<Stream>[], nodeId: string) {
frameId: f.frameId,
autoStart: f.autoStart,
name: f.title || fs.title || s.title,
deviceId: f.deviceId,
});
}
}
......@@ -37,6 +38,19 @@ function framesByNodeId(streams: MongooseDocument<Stream>[], nodeId: string) {
return frames;
}
interface IDevice {
type: string;
id: string;
name: string;
}
function hasDevice(devices: IDevice[], device?: string) {
if (!device) {
return true;
}
return devices.some((d) => d.id === device);
}
@Service()
export default class StreamService {
@Inject(Stream)
......@@ -58,7 +72,7 @@ export default class StreamService {
if (data.event === 'thumbnail') {
const stream = await this.streams.findOne({ uri: data.id });
if (stream) {
await redisSet(`stream:thumbnail:${stream.id}`, data.data);
await redisSet(`stream:thumbnail:${stream.id}:${data.framesetId}:${data.frameId}`, data.data);
}
}
});
......@@ -79,12 +93,13 @@ export default class StreamService {
const selectedFrames = framesByNodeId(streams, data.id);
console.log('CHANGE STREAM', selectedFrames);
const devices = data.devices ? JSON.parse(data.devices) : [];
for (const sf of selectedFrames) {
if (data.event === 'connect' && sf.autoStart && hasDevice(devices, sf.deviceId)) {
this.updateStats(sf.uri, sf.framesetId, sf.frameId, {
active: data.event === 'connect',
active: true,
});
if (data.event === 'connect' && sf.autoStart) {
sendStreamUpdateEvent({
event: 'start',
id: sf.uri,
......@@ -93,6 +108,10 @@ export default class StreamService {
framesetId: sf.framesetId,
frameId: sf.frameId,
});
} else if (data.event === 'disconnect') {
this.updateStats(sf.uri, sf.framesetId, sf.frameId, {
active: false,
});
}
}
}
......@@ -170,10 +189,10 @@ export default class StreamService {
).deletedCount;
}
async getThumbnail(id: string, groups: string[]) {
async getThumbnail(id: string, frameset: number, frame: number, groups: string[]) {
const stream = await this.streams.findOne({ _id: id, groups: { $in: groups } });
if (stream) {
const thumb = await redisGet<string>(`stream:thumbnail:${id}`);
const thumb = await redisGet<string>(`stream:thumbnail:${id}:${frameset}:${frame}`);
return thumb ? Buffer.from(thumb, 'base64') : null;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment