Skip to content

Commit ef3c81c

Browse files
committed
fix(add_sqlite3_for_persistent_storage): decoupled frontend and backend with regards to the playlist, so playlist runs in the node-server and emits messages to the frontend when the playlist changes
1 parent 43ef131 commit ef3c81c

File tree

9 files changed

+378
-155
lines changed

9 files changed

+378
-155
lines changed

app/playlist.js

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
const _ = require("lodash");
2+
const {discoveries} = require("./discovery");
3+
const {getPlaylistFromDB} = require("../db/controllers/playlist");
4+
5+
const WebSocket = require('ws');
6+
const http = require('http');
7+
const {
8+
v4: uuidv4,
9+
} = require('uuid');
10+
11+
const playlist = {};
12+
exports.playlist = playlist;
13+
14+
let currentPlaylist = []
15+
let currentPlaylistData = []
16+
let pixelBlazeData = []
17+
let pixelBlazeIds = []
18+
let playlistTimeout = null
19+
let playlistLoopTimeout = null
20+
let initInterval = null
21+
init = () => {
22+
getPlaylistFromDB()
23+
.then((data) => {
24+
currentPlaylist = [] // resetting current play so it doesn't grow to infinity
25+
currentPlaylist.push(...data) // adding new playlist items to list
26+
if (JSON.stringify(currentPlaylist) !== JSON.stringify(currentPlaylistData)) {
27+
currentPlaylistData = []
28+
currentPlaylistData.push(...data)
29+
}
30+
})
31+
.catch('there was an error gathering playlist details')
32+
33+
// gather pixelblaze data
34+
pixelBlazeData = _.map(discoveries, function (v, k) {
35+
let res = _.pick(v, ['lastSeen', 'address']);
36+
_.assign(res, v.controller.props);
37+
return res;
38+
})
39+
pixelBlazeIds = _.map(pixelBlazeData, 'id')
40+
}
41+
42+
initInterval = setInterval(init ,100)
43+
44+
const server = http.createServer();
45+
const port = 1890;
46+
const playlistServer = new WebSocket.Server({server});
47+
server.listen(port, () => {
48+
console.log(`Playlist server is running on port ${port}`);
49+
});
50+
51+
const playlistClients = {};
52+
53+
playlistServer.on('connection', (connection) => {
54+
// Generate a unique code for every user
55+
const clientId = uuidv4()
56+
console.log(`Recieved a new connection.`);
57+
58+
// Store the new connection and handle messages
59+
playlistClients[clientId] = connection;
60+
console.log(`${clientId} connected.`);
61+
connection.on('message', async (data) => {
62+
let message
63+
try {
64+
message = JSON.parse(data);
65+
} catch (err) {
66+
sendError(playlistServer, `Wrong format ${err}`)
67+
return
68+
}
69+
if (message.type === 'LAUNCH_PLAYLIST_NOW') {
70+
console.log('received launch playlist now message!')
71+
await runPlaylistLoopNow()
72+
}
73+
})
74+
});
75+
76+
const sendError = (ws, message) => {
77+
const messageObject = {
78+
type: 'ERROR',
79+
payload: message,
80+
};
81+
ws.send(JSON.stringify(messageObject));
82+
};
83+
const broadcastMessage = (json) => {
84+
const data = JSON.stringify(json);
85+
for(let userId in playlistClients) {
86+
let playlistClient = playlistClients[userId];
87+
if(playlistClient.readyState === WebSocket.OPEN) {
88+
playlistClient.send(data);
89+
}
90+
};
91+
};
92+
const sendPattern = (pattern) => {
93+
const name = pattern.name
94+
_.each(pixelBlazeIds, async id => {
95+
id = String(id);
96+
let controller = discoveries[id] && discoveries[id].controller;
97+
if (controller) {
98+
const command = {
99+
programName: pattern.name
100+
}
101+
await controller.setCommand(command);
102+
}
103+
})
104+
let message = {
105+
currentRunningPattern: name,
106+
currentPlaylist: currentPlaylist
107+
}
108+
broadcastMessage(message)
109+
}
110+
111+
const delaySendPattern = (pattern) => {
112+
return new Promise((resolve) => {
113+
resolve(sendPattern(pattern))
114+
})
115+
}
116+
const iterateOnPlaylist = async () => {
117+
for (let index = 0; index < currentPlaylist.length; index++) {
118+
const pattern = currentPlaylist[index]
119+
await delaySendPattern(pattern)
120+
await new Promise(resolve => {
121+
playlistTimeout = setTimeout(resolve, pattern.duration * 1000)
122+
});
123+
}
124+
}
125+
module.exports.playlistLoop = async () => {
126+
while (true) {
127+
await new Promise(resolve => {
128+
playlistLoopTimeout = setTimeout(resolve, 100)
129+
});
130+
if(pixelBlazeIds.length) {
131+
await iterateOnPlaylist()
132+
}
133+
initInterval = null
134+
playlistTimeout = null
135+
playlistLoopTimeout = null
136+
}
137+
}
138+
const runPlaylistLoopNow = async () => {
139+
clearInterval(initInterval)
140+
clearInterval(playlistTimeout)
141+
clearInterval(playlistLoopTimeout)
142+
143+
await this.playlistLoop()
144+
}
145+
this.playlistLoop()

db/api/router.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11

2-
const playlistDbRoutes = require('../controllers/playlist-controllers')
2+
const playlistDbRoutes = require('../controllers/playlist')
33

44
// Create router
55
module.exports = function (app) {
66
app.get('/playlist/getPatterns', playlistDbRoutes.getAllPlaylistPatterns)
77
app.post('/playlist/addPattern', playlistDbRoutes.addPatternToPlaylist)
8-
app.put('/playlist/removePattern', playlistDbRoutes.removePatternToPlaylist)
8+
app.put('/playlist/removePattern', playlistDbRoutes.removePatternFromPlaylist)
99
app.put('/playlist/newPlaylist', playlistDbRoutes.newPlaylist)
1010
}

db/controllers/playlist-controllers.js renamed to db/controllers/playlist.js

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,27 @@
11
const knex = require('../db')
2+
const _ = require("lodash");
3+
const playlist_table = 'playlist'
24

3-
exports.getAllPlaylistPatterns = async (req, res) => {
4-
knex
5+
exports.doesPatternExistInPlaylist = async (req) => {
6+
return await knex
7+
.select('*')
8+
.from(playlist_table)
9+
.where('name', "=", req.body.name)
10+
.then((res) => {
11+
if (res.length === 0) return false
12+
if (res.length !== 0) return true
13+
})
14+
}
15+
exports.getPlaylistFromDB = async () => {
16+
return await knex
517
.select('*')
6-
.from('playlist')
7-
.then(playlistData => {
18+
.from(playlist_table)
19+
.then((data) => {
20+
return data
21+
})
22+
}
23+
exports.getAllPlaylistPatterns = async (req, res) => {
24+
this.getPlaylistFromDB().then(playlistData => {
825
res.status(200)
926
.json(playlistData);
1027
})
@@ -15,21 +32,17 @@ exports.getAllPlaylistPatterns = async (req, res) => {
1532
}
1633

1734
exports.addPatternToPlaylist = async (req, res) => {
18-
const doesPatternExistInPlaylist = await knex
19-
.select('*')
20-
.from('playlist')
21-
.where('name', "=", req.body.name)
22-
.then((res) => {
23-
if(res.length === 0) return false
24-
if(res.length !== 0) return true
35+
const doesPatternExistInPlaylist = await this.doesPatternExistInPlaylist(req)
36+
.then((condition) => {
37+
return condition
2538
})
2639
// update existing pattern in playlist
2740
if(doesPatternExistInPlaylist) {
28-
knex
41+
await knex
2942
.update({
3043
duration: req.body.duration,
3144
})
32-
.into('playlist')
45+
.into(playlist_table)
3346
.where(
3447
'name', '=', req.body.name
3548
)
@@ -42,12 +55,12 @@ exports.addPatternToPlaylist = async (req, res) => {
4255
}
4356
// insert new pattern into playlist
4457
if(!doesPatternExistInPlaylist) {
45-
knex
58+
await knex
4659
.insert({
4760
name: req.body.name,
4861
duration: req.body.duration,
4962
})
50-
.into('playlist')
63+
.into(playlist_table)
5164
.then(() => {
5265
res.status(200)
5366
.json({message: `Pattern \'${req.body.name}\' with a duration of ${req.body.duration} created.`})
@@ -59,9 +72,9 @@ exports.addPatternToPlaylist = async (req, res) => {
5972
}
6073
}
6174

62-
exports.removePatternToPlaylist = async (req, res) => {
63-
knex
64-
.into('playlist')
75+
exports.removePatternFromPlaylist = async (req, res) => {
76+
await knex
77+
.into(playlist_table)
6578
.where('name', req.body.name)
6679
.del()
6780
.then( () => {
@@ -78,15 +91,27 @@ exports.removePatternToPlaylist = async (req, res) => {
7891
}
7992

8093
exports.newPlaylist = async (req, res) => {
81-
await knex
82-
.into('playlist')
83-
.where('id','!=', 'null')
84-
.del()
94+
await knex.transaction(async trx => {
95+
//clear table first
96+
await knex
97+
.into(playlist_table)
98+
.where('id','!=', 'null')
99+
.del()
100+
.transacting(trx);
101+
// insert new pattern
102+
await knex
103+
.insert({
104+
name: req.body.name,
105+
duration: req.body.duration,
106+
})
107+
.into(playlist_table)
108+
.transacting(trx);
109+
})
85110
.then( () => {
86-
res.status(200)
87-
.json({ message: `Creating a new playlist with pattern '${req.body.name}' from playlist.`});
88-
}
89-
)
111+
res.status(200)
112+
.json({ message: `Creating a new playlist with pattern '${req.body.name}' from playlist.`});
113+
}
114+
)
90115
.catch(err => {
91116
res.status(500)
92117
.json({

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"react-dom": "^16.13.1",
1717
"react-scripts": "^3.4.1",
1818
"sqlite3": "^5.1.6",
19+
"uuid": "^9.0.0",
1920
"ws": "^7.4.6"
2021
},
2122
"scripts": {

server.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const app = express()
44
const bodyParser = require('body-parser');
55
const compression = require('compression');
66
const repl = require('repl');
7-
7+
require("./app/playlist");
88
discovery.start({
99
host: '0.0.0.0',
1010
port: 1889
@@ -26,6 +26,7 @@ app.listen(PORT)
2626

2727

2828
const r = repl.start('> ');
29+
2930
r.on('exit', () => {
3031
console.log('Received "exit" event from repl!');
3132
process.exit();

0 commit comments

Comments
 (0)