karaoke-eternal 2.0.0-beta.6 → 2.0.0-beta.7

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.
@@ -26,11 +26,12 @@ class FileScanner extends Scanner {
26
26
  async scan(pathId) {
27
27
  const dir = this.paths.entities[pathId]?.path;
28
28
  const validMediaIds = [];
29
+ const stats = { new: 0, removed: 0, existing: 0 };
29
30
  let files; // { file, stats }[]
30
31
  let prevDir;
31
32
  if (!dir) {
32
33
  log.error('invalid pathId: %s', pathId);
33
- return;
34
+ return stats;
34
35
  }
35
36
  log.info('Searching: %s', dir);
36
37
  this.emitStatus(`Searching: ${dir}`, 0);
@@ -40,7 +41,7 @@ class FileScanner extends Scanner {
40
41
  }
41
42
  catch (err) {
42
43
  log.error(` => ${err.message} (path offline)`);
43
- return;
44
+ return stats;
44
45
  }
45
46
  for (let i = 0; i < files.length; i++) {
46
47
  const curDir = path.dirname(files[i].file);
@@ -51,24 +52,30 @@ class FileScanner extends Scanner {
51
52
  this.parser = new MetaParser(cfg);
52
53
  }
53
54
  log.info('[%s/%s] %s', i + 1, files.length, files[i].file);
54
- this.emitStatus(`Processing (${i + 1} of ${files.length})`, (i + 1) / files.length);
55
+ this.emitStatus(`Scanning (${i + 1} of ${files.length})`, (i + 1) / files.length);
55
56
  // process file
56
57
  try {
57
58
  const res = await this.process(files[i], pathId);
58
59
  validMediaIds.push(res.mediaId);
60
+ if (res.isNew)
61
+ stats.new++;
62
+ else
63
+ stats.existing++;
59
64
  }
60
65
  catch (err) {
61
66
  log.warn(` => ${err.message}`);
62
67
  }
63
68
  if (this.isCanceling) {
64
69
  this.emitStatus('Stopped', 100, false);
65
- return;
70
+ return stats;
66
71
  }
67
72
  } // end for
68
- log.info('Processed %s valid media files', validMediaIds.length.toLocaleString());
73
+ log.info('Scanned %s valid media files', validMediaIds.length.toLocaleString());
69
74
  log.info('Searching for invalid media entries');
70
75
  const numRemoved = await this.removeInvalid(pathId, validMediaIds);
76
+ stats.removed = numRemoved;
71
77
  log.info(`Removed ${numRemoved} invalid media entries`);
78
+ return stats;
72
79
  }
73
80
  async process({ file }, pathId) {
74
81
  let buffer = await fsPromises.readFile(file);
@@ -75,7 +75,11 @@ m.set('remove quotes', (ctx, next) => {
75
75
  // some artist-specific tweaks
76
76
  m.set('artist tweaks', (ctx, next) => {
77
77
  // Last, First [Middle] -> First [Middle] Last
78
- ctx.artist = ctx.artist.replace(/^(\w+), (\w+ ?\w+)$/ig, '$2 $1');
78
+ // Use negative lookahead to avoid matching when second part starts with an article (e.g., "Tyler, The Creator")
79
+ const articles = Array.isArray(ctx.cfg.articles) ? ctx.cfg.articles.join(' |') + ' ' : '';
80
+ const articleLookahead = articles ? `(?!${articles})` : '';
81
+ const pattern = new RegExp(`^([\\w ]+), ${articleLookahead}(\\w+ ?\\w+)$`, 'i');
82
+ ctx.artist = ctx.artist.replace(pattern, '$2 $1');
79
83
  // featuring/feat/ft -> ft.
80
84
  ctx.artist = ctx.artist.replace(/ featuring /i, ' ft. ');
81
85
  ctx.artist = ctx.artist.replace(/ f(ea)?t\.? /i, ' ft. ');
@@ -139,7 +143,7 @@ function moveArticles(str, articles) {
139
143
  if (new RegExp(`^${search}`, 'i').test(str)) {
140
144
  const parens = /[([{].*$/.exec(str);
141
145
  if (parens) {
142
- str = str.substring(search.length, parens.index - search.length)
146
+ str = str.substring(search.length, parens.index)
143
147
  .trim() + `, ${article} ${parens[0]}`;
144
148
  }
145
149
  else {
@@ -346,9 +346,8 @@ router.post('/setup', async (ctx) => {
346
346
  router.get('/user/:userId/image', async (ctx) => {
347
347
  const targetId = parseInt(ctx.params.userId, 10);
348
348
  if (ctx.user.userId !== targetId && !ctx.user.isAdmin) {
349
- // ensure users are in the same room
350
- const sockets = await ctx.io.in(Rooms.prefix(ctx.user.roomId)).fetchSockets();
351
- if (!sockets.some(s => s?.user && s?.user.userId === targetId)) {
349
+ // ensure target user has been in the same room
350
+ if (!Rooms.hasUserBeenInRoom(ctx.user.roomId, targetId)) {
352
351
  ctx.throw(403);
353
352
  }
354
353
  }
@@ -42,8 +42,13 @@ let IPC;
42
42
  log.debug('parsed pathIds: %s', pathIds);
43
43
  q.queue(pathIds);
44
44
  })();
45
- // @todo
45
+ const totals = { new: 0, existing: 0, removed: 0 };
46
46
  function onIteration(stats) {
47
+ if (stats) {
48
+ totals.new += stats.new;
49
+ totals.existing += stats.existing;
50
+ totals.removed += stats.removed;
51
+ }
47
52
  return stats;
48
53
  }
49
54
  function onDone() {
@@ -52,7 +57,7 @@ function onDone() {
52
57
  payload: {
53
58
  isScanning: false,
54
59
  pct: 100,
55
- text: 'Finished',
60
+ text: `Scan finished (${totals.new} new, ${totals.removed} removed)`,
56
61
  },
57
62
  });
58
63
  process.exit(0); // eslint-disable-line n/no-process-exit
@@ -111,8 +111,9 @@ export default function (io, jwtKey) {
111
111
  if (typeof sock.user.roomId !== 'number')
112
112
  return;
113
113
  // beyond this point assumes there is a room
114
- // add user to room
114
+ // add user to room and track membership
115
115
  sock.join(Rooms.prefix(sock.user.roomId));
116
+ Rooms.trackUser(sock.user.roomId, sock.user.userId);
116
117
  // if there's a player in room, emit its last known status
117
118
  // @todo this just emits the first status found
118
119
  for (const s of io.of('/').sockets.values()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karaoke-eternal",
3
- "version": "2.0.0-beta.6",
3
+ "version": "2.0.0-beta.7",
4
4
  "description": "Open karaoke party system",
5
5
  "keywords": [
6
6
  "karaoke",
@@ -57,7 +57,7 @@
57
57
  "bcrypt": "^5.1.1",
58
58
  "ctx-compose": "^3.0.0",
59
59
  "electron-log": "^5.4.3",
60
- "fs-extra": "^11.3.2",
60
+ "fs-extra": "^11.3.3",
61
61
  "json-e": "^4.8.0",
62
62
  "json5": "^2.2.3",
63
63
  "jsonwebtoken": "^9.0.3",
@@ -86,7 +86,7 @@
86
86
  "@babel/preset-typescript": "^7.28.5",
87
87
  "@hello-pangea/dnd": "^18.0.1",
88
88
  "@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
89
- "@reduxjs/toolkit": "^2.11.0",
89
+ "@reduxjs/toolkit": "^2.11.2",
90
90
  "@stylistic/eslint-plugin": "^5.6.1",
91
91
  "@types/bcrypt": "^6.0.0",
92
92
  "@types/blueimp-load-image": "^5.16.6",
@@ -100,7 +100,7 @@
100
100
  "@types/koa-static": "^4.0.4",
101
101
  "@types/react": "^19.2.7",
102
102
  "@types/react-dom": "^19.2.3",
103
- "@types/react-highlight-words": "^0.20.0",
103
+ "@types/react-highlight-words": "^0.20.1",
104
104
  "@types/react-transition-group": "^4.4.12",
105
105
  "@types/react-window": "^1.8.8",
106
106
  "@types/redux-optimistic-ui": "^0.4.14",
@@ -117,13 +117,13 @@
117
117
  "cdgraphics": "^7.0.0",
118
118
  "clsx": "^2.1.1",
119
119
  "css-loader": "^7.1.2",
120
- "eslint": "^9.39.1",
120
+ "eslint": "^9.39.2",
121
121
  "eslint-plugin-import": "^2.32.0",
122
122
  "eslint-plugin-n": "^17.23.1",
123
123
  "eslint-plugin-promise": "^7.2.1",
124
124
  "eslint-plugin-react": "^7.37.5",
125
125
  "eslint-plugin-react-hooks": "^7.0.1",
126
- "eslint-plugin-react-refresh": "^0.4.24",
126
+ "eslint-plugin-react-refresh": "^0.4.26",
127
127
  "fast-fuzzy": "^1.12.0",
128
128
  "file-loader": "^6.2.0",
129
129
  "gl-chromakey": "^2.0.0",
@@ -145,7 +145,7 @@
145
145
  "react-qrcode-logo": "^4.0.0",
146
146
  "react-redux": "^9.2.0",
147
147
  "react-refresh": "^0.18.0",
148
- "react-router": "^7.10.1",
148
+ "react-router": "^7.11.0",
149
149
  "react-swipeable": "^7.0.2",
150
150
  "react-transition-group": "^4.4.5",
151
151
  "react-window": "^2.2.3",
@@ -156,11 +156,11 @@
156
156
  "socket.io-client": "^4.8.1",
157
157
  "tsx": "^4.21.0",
158
158
  "typescript": "^5.9.3",
159
- "typescript-eslint": "^8.48.1",
159
+ "typescript-eslint": "^8.50.0",
160
160
  "typescript-plugin-css-modules": "^5.2.0",
161
161
  "use-long-press": "^3.3.0",
162
162
  "use-resize-observer": "^9.1.0",
163
- "webpack": "^5.103.0",
163
+ "webpack": "^5.104.1",
164
164
  "webpack-cli": "^6.0.1",
165
165
  "webpack-dev-middleware": "^7.4.5",
166
166
  "webpack-hot-middleware": "^2.26.1",