karaoke-eternal 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +49 -0
  3. package/assets/app.ico +0 -0
  4. package/assets/app.png +0 -0
  5. package/assets/favicon.ico +0 -0
  6. package/assets/mic-blackTemplate.png +0 -0
  7. package/assets/mic-blackTemplate@2x.png +0 -0
  8. package/assets/mic-white.png +0 -0
  9. package/assets/mic-white@2x.png +0 -0
  10. package/assets/robots.txt +2 -0
  11. package/build/267.4be526e3a94d53aeceae.js +1 -0
  12. package/build/591.4be526e3a94d53aeceae.js +1 -0
  13. package/build/598.4be526e3a94d53aeceae.css +5 -0
  14. package/build/598.4be526e3a94d53aeceae.js +1 -0
  15. package/build/799.4be526e3a94d53aeceae.js +1 -0
  16. package/build/7ce9eb3fe454f54745a4.woff2 +0 -0
  17. package/build/845.4be526e3a94d53aeceae.css +132 -0
  18. package/build/845.4be526e3a94d53aeceae.js +1 -0
  19. package/build/a35814dd9eb496e3d7cc.woff2 +0 -0
  20. package/build/e419b95dccb58b362811.woff2 +0 -0
  21. package/build/index.html +1 -0
  22. package/build/licenses.txt +1400 -0
  23. package/build/main.4be526e3a94d53aeceae.css +2034 -0
  24. package/build/main.4be526e3a94d53aeceae.js +1 -0
  25. package/package.json +144 -0
  26. package/server/Library/Library.js +340 -0
  27. package/server/Library/index.js +3 -0
  28. package/server/Library/ipc.js +18 -0
  29. package/server/Library/router.js +27 -0
  30. package/server/Library/socket.js +47 -0
  31. package/server/Media/Media.js +207 -0
  32. package/server/Media/index.js +3 -0
  33. package/server/Media/ipc.js +19 -0
  34. package/server/Media/router.js +99 -0
  35. package/server/Player/socket.js +78 -0
  36. package/server/Prefs/Prefs.js +165 -0
  37. package/server/Prefs/index.js +3 -0
  38. package/server/Prefs/router.js +124 -0
  39. package/server/Prefs/socket.js +68 -0
  40. package/server/Queue/Queue.js +208 -0
  41. package/server/Queue/index.js +3 -0
  42. package/server/Queue/socket.js +99 -0
  43. package/server/Rooms/Rooms.js +114 -0
  44. package/server/Rooms/index.js +3 -0
  45. package/server/Rooms/router.js +146 -0
  46. package/server/Scanner/FileScanner/FileScanner.js +225 -0
  47. package/server/Scanner/FileScanner/getConfig.js +35 -0
  48. package/server/Scanner/FileScanner/getFiles.js +63 -0
  49. package/server/Scanner/FileScanner/index.js +3 -0
  50. package/server/Scanner/MetaParser/MetaParser.js +49 -0
  51. package/server/Scanner/MetaParser/defaultMiddleware.js +197 -0
  52. package/server/Scanner/MetaParser/index.js +3 -0
  53. package/server/Scanner/Scanner.js +33 -0
  54. package/server/User/User.js +139 -0
  55. package/server/User/index.js +3 -0
  56. package/server/User/router.js +442 -0
  57. package/server/lib/Database.js +55 -0
  58. package/server/lib/IPCBridge.js +115 -0
  59. package/server/lib/Log.js +71 -0
  60. package/server/lib/bcrypt.js +24 -0
  61. package/server/lib/cli.js +136 -0
  62. package/server/lib/electron.js +81 -0
  63. package/server/lib/getCdgName.js +20 -0
  64. package/server/lib/getDevMiddleware.js +51 -0
  65. package/server/lib/getFolders.js +10 -0
  66. package/server/lib/getHotMiddleware.js +27 -0
  67. package/server/lib/getIPAddress.js +16 -0
  68. package/server/lib/getPermutations.js +21 -0
  69. package/server/lib/getWindowsDrives.js +30 -0
  70. package/server/lib/parseCookie.js +12 -0
  71. package/server/lib/pushQueuesAndLibrary.js +29 -0
  72. package/server/lib/schemas/001-initial-schema.sql +98 -0
  73. package/server/lib/schemas/002-replaygain.sql +9 -0
  74. package/server/lib/schemas/003-queue-linked-list.sql +16 -0
  75. package/server/main.js +135 -0
  76. package/server/scannerWorker.js +58 -0
  77. package/server/serverWorker.js +242 -0
  78. package/server/socket.js +173 -0
  79. package/shared/actionTypes.js +103 -0
@@ -0,0 +1,173 @@
1
+ const log = require('./lib/Log')('server')
2
+ const jwtVerify = require('jsonwebtoken').verify
3
+
4
+ const parseCookie = require('./lib/parseCookie')
5
+ const Library = require('./Library')
6
+ const LibrarySocket = require('./Library/socket')
7
+ const PlayerSocket = require('./Player/socket')
8
+ const Prefs = require('./Prefs')
9
+ const PrefsSocket = require('./Prefs/socket')
10
+ const Rooms = require('./Rooms')
11
+ const Queue = require('./Queue')
12
+ const QueueSocket = require('./Queue/socket')
13
+ const {
14
+ LIBRARY_PUSH,
15
+ QUEUE_PUSH,
16
+ STARS_PUSH,
17
+ STAR_COUNTS_PUSH,
18
+ PLAYER_STATUS,
19
+ PLAYER_LEAVE,
20
+ PREFS_PUSH,
21
+ SOCKET_AUTH_ERROR,
22
+ _ERROR,
23
+ } = require('../shared/actionTypes')
24
+
25
+ const handlers = {
26
+ ...LibrarySocket,
27
+ ...QueueSocket,
28
+ ...PlayerSocket,
29
+ ...PrefsSocket,
30
+ }
31
+
32
+ module.exports = function (io, jwtKey) {
33
+ io.on('connection', async (sock) => {
34
+ const { keToken } = parseCookie(sock.handshake.headers.cookie)
35
+ const clientLibraryVersion = parseInt(sock.handshake.query.library, 10)
36
+ const clientStarsVersion = parseInt(sock.handshake.query.stars, 10)
37
+
38
+ // authenticate the JWT sent via cookie in http handshake
39
+ try {
40
+ sock.user = jwtVerify(keToken, jwtKey)
41
+
42
+ // success
43
+ log.verbose('%s (%s) connected from %s', sock.user.name, sock.id, sock.handshake.address)
44
+ } catch (err) {
45
+ io.to(sock.id).emit('action', {
46
+ type: SOCKET_AUTH_ERROR,
47
+ })
48
+
49
+ sock.user = null
50
+ sock.disconnect()
51
+ log.verbose('disconnected %s (%s)', sock.handshake.address, err.message)
52
+ return
53
+ }
54
+
55
+ // attach disconnect handler
56
+ sock.on('disconnect', reason => {
57
+ log.verbose('%s (%s) disconnected (%s)',
58
+ sock.user.name, sock.id, reason
59
+ )
60
+
61
+ if (typeof sock.user.roomId !== 'number') return
62
+
63
+ // beyond this point assumes there is a room
64
+
65
+ log.verbose('%s (%s) left room %s (%s; %s in room)',
66
+ sock.user.name, sock.id, sock.user.roomId, reason, sock.adapter.rooms.size
67
+ )
68
+
69
+ // any players left in room?
70
+ if (!Rooms.isPlayerPresent(io, sock.user.roomId)) {
71
+ io.to(Rooms.prefix(sock.user.roomId)).emit('action', {
72
+ type: PLAYER_LEAVE,
73
+ payload: { socketId: sock.id },
74
+ })
75
+ }
76
+ })
77
+
78
+ // attach action handler
79
+ sock.on('action', async (action, acknowledge) => {
80
+ const { type } = action
81
+
82
+ if (!sock.user) {
83
+ return acknowledge({
84
+ type: SOCKET_AUTH_ERROR,
85
+ })
86
+ }
87
+
88
+ if (typeof handlers[type] !== 'function') {
89
+ log.error('No handler for socket action: %s', type)
90
+ return
91
+ }
92
+
93
+ try {
94
+ await handlers[type](sock, action, acknowledge)
95
+ } catch (err) {
96
+ log.error(err)
97
+
98
+ return acknowledge({
99
+ type: type + _ERROR,
100
+ error: `Error in ${type}: ${err.message}`,
101
+ })
102
+ }
103
+ })
104
+
105
+ // push prefs (admin only)
106
+ if (sock.user.isAdmin) {
107
+ log.verbose('pushing prefs to %s (%s)', sock.user.name, sock.id)
108
+ io.to(sock.id).emit('action', {
109
+ type: PREFS_PUSH,
110
+ payload: await Prefs.get(),
111
+ })
112
+ }
113
+
114
+ // push library (only if client's is outdated)
115
+ if (clientLibraryVersion !== Library.cache.version) {
116
+ log.verbose('pushing library to %s (%s) (client=%s, server=%s)',
117
+ sock.user.name, sock.id, clientLibraryVersion, Library.cache.version)
118
+
119
+ io.to(sock.id).emit('action', {
120
+ type: LIBRARY_PUSH,
121
+ payload: await Library.get(),
122
+ })
123
+ }
124
+
125
+ // push user's stars
126
+ io.to(sock.id).emit('action', {
127
+ type: STARS_PUSH,
128
+ payload: await Library.getUserStars(sock.user.userId),
129
+ })
130
+
131
+ // push star counts (only if client's is outdated)
132
+ if (clientStarsVersion !== Library.starCountsCache.version) {
133
+ log.verbose('pushing star counts to %s (%s) (client=%s, server=%s)',
134
+ sock.user.name, sock.id, clientStarsVersion, Library.starCountsCache.version)
135
+
136
+ io.to(sock.id).emit('action', {
137
+ type: STAR_COUNTS_PUSH,
138
+ payload: await Library.getStarCounts(),
139
+ })
140
+ }
141
+
142
+ // it's possible for an admin to not be in a room
143
+ if (typeof sock.user.roomId !== 'number') return
144
+
145
+ // beyond this point assumes there is a room
146
+
147
+ // add user to room
148
+ sock.join(Rooms.prefix(sock.user.roomId))
149
+
150
+ // if there's a player in room, emit its last known status
151
+ // @todo this just emits the first status found
152
+ for (const s of io.of('/').sockets.values()) {
153
+ if (s.user && s.user.roomId === sock.user.roomId && s._lastPlayerStatus) {
154
+ io.to(sock.id).emit('action', {
155
+ type: PLAYER_STATUS,
156
+ payload: s._lastPlayerStatus,
157
+ })
158
+
159
+ break
160
+ }
161
+ }
162
+
163
+ log.verbose('%s (%s) joined room %s (%s in room)',
164
+ sock.user.name, sock.id, sock.user.roomId, sock.adapter.rooms.size
165
+ )
166
+
167
+ // send room's queue
168
+ io.to(sock.id).emit('action', {
169
+ type: QUEUE_PUSH,
170
+ payload: await Queue.get(sock.user.roomId),
171
+ })
172
+ })
173
+ }
@@ -0,0 +1,103 @@
1
+ module.exports = {
2
+ // sockets
3
+ SOCKET_REQUEST_CONNECT: 'user/SOCKET_REQUEST_CONNECT',
4
+ SOCKET_AUTH_ERROR: 'user/SOCKET_AUTH_ERROR',
5
+ // library
6
+ LIBRARY_FILTER_STRING: 'library/FILTER_KEYWORD',
7
+ LIBRARY_FILTER_STRING_RESET: 'library/FILTER_KEYWORD_CLEAR',
8
+ LIBRARY_FILTER_TOGGLE_STARRED: 'library/TOGGLE_FILTER_STARRED',
9
+ TOGGLE_ARTIST_EXPANDED: 'library/TOGGLE_ARTIST_EXPANDED',
10
+ TOGGLE_ARTIST_RESULT_EXPANDED: 'library/TOGGLE_ARTIST_RESULT_EXPANDED',
11
+ SCROLL_ARTISTS: 'library/SCROLL_ARTISTS',
12
+ SONG_STARRED: 'library/SONG_STARRED',
13
+ SONG_UNSTARRED: 'library/SONG_UNSTARRED',
14
+ STAR_COUNTS_PUSH: 'library/STAR_COUNTS_PUSH',
15
+ LIBRARY_PUSH: 'library/PUSH',
16
+ LIBRARY_PUSH_SONG: 'library/PUSH_SONG',
17
+ // queue
18
+ QUEUE_ADD: 'server/QUEUE_ADD',
19
+ QUEUE_MOVE: 'server/QUEUE_MOVE',
20
+ QUEUE_PUSH: 'queue/PUSH',
21
+ QUEUE_REMOVE: 'server/QUEUE_REMOVE',
22
+ // player internal
23
+ PLAYER_UPDATE: 'player/UPDATE',
24
+ // player room commands
25
+ PLAYER_CMD_OPTIONS: 'player/CMD_OPTIONS',
26
+ PLAYER_CMD_NEXT: 'player/CMD_NEXT',
27
+ PLAYER_CMD_PAUSE: 'player/CMD_PAUSE',
28
+ PLAYER_CMD_PLAY: 'player/CMD_PLAY',
29
+ PLAYER_CMD_VOLUME: 'player/CMD_VOLUME',
30
+ PLAYER_REQ_OPTIONS: 'server/PLAYER_REQ_OPTIONS',
31
+ PLAYER_REQ_PLAY: 'server/PLAYER_REQ_PLAY',
32
+ PLAYER_REQ_PAUSE: 'server/PLAYER_REQ_PAUSE',
33
+ PLAYER_REQ_NEXT: 'server/PLAYER_REQ_NEXT',
34
+ PLAYER_REQ_VOLUME: 'server/PLAYER_REQ_VOLUME',
35
+ PLAYER_EMIT_STATUS: 'server/PLAYER_EMIT_STATUS',
36
+ PLAYER_EMIT_LEAVE: 'server/PLAYER_EMIT_LEAVE',
37
+ // player events
38
+ PLAYER_STATUS: 'status/PLAYER_STATUS',
39
+ PLAYER_ERROR: 'status/PLAYER_ERROR',
40
+ PLAYER_LEAVE: 'status/PLAYER_LEAVE',
41
+ PLAYER_LOAD: 'status/PLAYER_LOAD',
42
+ PLAYER_PLAY: 'status/PLAYER_PLAY',
43
+ PLAYER_VISUALIZER_ERROR: 'status/PLAYER_VISUALIZER_ERROR',
44
+ // user
45
+ LOGIN: 'user/LOGIN',
46
+ LOGOUT: 'user/LOGOUT',
47
+ CREATE_ACCOUNT: 'user/CREATE_ACCOUNT',
48
+ UPDATE_ACCOUNT: 'user/UPDATE_ACCOUNT',
49
+ REQUEST_ACCOUNT: 'user/REQUEST_ACCOUNT',
50
+ REQUEST_ROOMS: 'rooms/REQUEST_ROOMS',
51
+ ROOM_UPDATE: 'rooms/UPDATE',
52
+ ROOM_CREATE: 'rooms/CREATE',
53
+ ROOM_REMOVE: 'rooms/REMOVE',
54
+ ROOM_EDITOR_OPEN: 'rooms/EDITOR_OPEN',
55
+ ROOM_EDITOR_CLOSE: 'rooms/EDITOR_CLOSE',
56
+ ROOM_FILTER_STATUS: 'rooms/TOGGLE_SHOW_ALL',
57
+ STAR_SONG: 'server/STAR_SONG',
58
+ UNSTAR_SONG: 'server/UNSTAR_SONG',
59
+ STARS_PUSH: 'user/STARS_PUSH',
60
+ // prefs
61
+ PREFS_RECEIVE: 'prefs/RECEIVE',
62
+ PREFS_REQUEST: 'prefs/REQUEST',
63
+ PREFS_SET: 'server/PREFS_SET',
64
+ PREFS_SET_PATH_PRIORITY: 'server/PREFS_SET_PATH_PRIORITY',
65
+ PREFS_PUSH: 'prefs/PREFS_PUSH',
66
+ PREFS_REQ_SCANNER_START: 'prefs/REQ_SCANNER_START',
67
+ PREFS_REQ_SCANNER_STOP: 'prefs/REQ_SCANNER_STOP',
68
+ // user management
69
+ USERS_CREATE: 'users/CREATE',
70
+ USERS_EDITOR_OPEN: 'users/EDITOR_OPEN',
71
+ USERS_EDITOR_CLOSE: 'users/EDITOR_CLOSE',
72
+ USERS_FILTER_ONLINE: 'users/FILTER_ONLINE',
73
+ USERS_FILTER_ROOM_ID: 'users/FILTER_ROOM_ID',
74
+ USERS_RECEIVE: 'users/RECEIVE',
75
+ USERS_REMOVE: 'users/REMOVE',
76
+ USERS_REQUEST: 'users/REQUEST',
77
+ USERS_UPDATE: 'users/UPDATE',
78
+ // ui
79
+ HEADER_HEIGHT_CHANGE: 'ui/HEADER_HEIGHT_CHANGE',
80
+ FOOTER_HEIGHT_CHANGE: 'ui/FOOTER_HEIGHT_CHANGE',
81
+ SHOW_ERROR_MESSAGE: 'ui/SHOW_ERROR_MESSAGE',
82
+ CLEAR_ERROR_MESSAGE: 'ui/CLEAR_ERROR_MESSAGE',
83
+ UI_WINDOW_RESIZE: 'ui/WINDOW_RESIZE',
84
+ // songInfo
85
+ SONG_INFO_REQUEST: 'songInfo/SONG_INFO_REQUEST',
86
+ SONG_INFO_SET_PREFERRED: 'songInfo/SET_PREFERRED',
87
+ SONG_INFO_CLOSE: 'songInfo/SONG_INFO_CLOSE',
88
+ // IPC messages
89
+ SCANNER_CMD_START: 'scannerWorker/CMD_START',
90
+ SCANNER_CMD_STOP: 'scannerWorker/CMD_STOP',
91
+ SCANNER_WORKER_LOG: 'scannerWorker/LOG',
92
+ SCANNER_WORKER_STATUS: 'scannerWorker/STATUS',
93
+ SERVER_WORKER_STATUS: 'serverWorker/STATUS',
94
+ SERVER_WORKER_ERROR: 'serverWorker/ERROR',
95
+ LIBRARY_MATCH_SONG: 'scannerWorker/LIBRARY_MATCH_SONG',
96
+ MEDIA_ADD: 'scannerWorker/MEDIA_ADD',
97
+ MEDIA_CLEANUP: 'scannerWorker/MEDIA_CLEANUP',
98
+ MEDIA_REMOVE: 'scannerWorker/MEDIA_REMOVE',
99
+ MEDIA_UPDATE: 'scannerWorker/MEDIA_UPDATE',
100
+ // misc
101
+ _SUCCESS: '_SUCCESS',
102
+ _ERROR: '_ERROR',
103
+ }