[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients docs.multi-scrobbler.app
deezer docker jellyfin koito lastfm listenbrainz maloja mopidy mpris music music-assistant plex scrobble self-hosted spotify subsonic tautulli youtube-music
0

Configure Feed

Select the types of activity you want to include in your feed.

refactor: autofix (eslint) prefer-const prefer-as-const and var rules

FoxxMD (Jul 9, 2026, 7:33 PM UTC) 43fd8ea8 ec3a7e2d

+89 -90
+1 -1
src/backend/common/Cache.ts
··· 216 216 217 217 protected initCacheable = async (cacheFor: string, primaryConfig: CacheConfig, secondaryConfig?: CacheConfig) => { 218 218 219 - let logger = childLogger(this.logger, cacheFor); 219 + const logger = childLogger(this.logger, cacheFor); 220 220 const providerHints = []; 221 221 if(primaryConfig.provider === false) { 222 222 const cache = new Cacheable({primary: noopKeyv});
+1 -1
src/backend/common/database/drizzle/entityUtils.ts
··· 56 56 return select.play; 57 57 } 58 58 59 - let res = select.play; 59 + const res = select.play; 60 60 // if(opts.includes('asPlay')) { 61 61 // res = asPlay(res); 62 62 // }
+4 -4
src/backend/common/database/drizzle/repositories/PlayHistoricalRepository.ts
··· 225 225 } else { 226 226 endRange = play.data.playDate.add(dateGranularity, 's'); 227 227 } 228 - let where: FindWhere<'playsHistorical'> = { 228 + const where: FindWhere<'playsHistorical'> = { 229 229 componentId, 230 230 playedAt: buildDateCompare(getTemporallyCloseDateCompareOp(play)), 231 231 }; ··· 266 266 bufferTime 267 267 } = opts; 268 268 269 - let query: FindMany<'playsHistorical'> = {}; 269 + const query: FindMany<'playsHistorical'> = {}; 270 270 271 - let where: FindWhere<'playsHistorical'> = { 271 + const where: FindWhere<'playsHistorical'> = { 272 272 componentId, 273 273 playedAt: buildDateCompare(getTemporallyCloseDateCompareOp(play, {bufferTime})), 274 274 }; ··· 291 291 // old way 292 292 // let where: Parameters<(ReturnType<typeof getDb>)['query']['plays']['findMany']>[0]['where'] = { 293 293 // }; 294 - let where: FindWhere<'playsHistorical'> = { 294 + const where: FindWhere<'playsHistorical'> = { 295 295 componentId: args.componentId 296 296 }; 297 297 if (args.seenAt !== undefined) {
+11 -11
src/backend/common/database/drizzle/repositories/PlayRepository.ts
··· 292 292 dateComparer = 'updatedAt' 293 293 } = opts; 294 294 295 - let where: FindWhere<'plays'> = { 295 + const where: FindWhere<'plays'> = { 296 296 component: { 297 297 id: componentId 298 298 }, ··· 342 342 343 343 const loggerDel = childLogger(this.logger, ['Retention', 'Delete']); 344 344 const loggerCom = childLogger(this.logger, ['Retention', 'Compact']); 345 - let summaryDelStates: string[] = []; 346 - let summaryCompactStates: string[] = []; 345 + const summaryDelStates: string[] = []; 346 + const summaryCompactStates: string[] = []; 347 347 348 348 loggerDel.debug('Starting cleanup...'); 349 349 for(const retentionType of retentionPlayTypes) { ··· 378 378 } 379 379 380 380 const compactTypes = retentionOpts.compact; 381 - let compactedFlags: CompactableProperty[] = []; 381 + const compactedFlags: CompactableProperty[] = []; 382 382 if(compactTypes.includes('input')) { 383 383 compactedFlags.push('input'); 384 384 } ··· 584 584 componentId = this.componentId, 585 585 hydrate 586 586 } = opts; 587 - let where: FindWhere<'plays'> = { 587 + const where: FindWhere<'plays'> = { 588 588 componentId 589 589 } 590 590 if(retries !== undefined) { ··· 634 634 } else { 635 635 endRange = play.data.playDate.add(dateGranularity, 's'); 636 636 } 637 - let where: FindWhere<'plays'> = { 637 + const where: FindWhere<'plays'> = { 638 638 componentId, 639 639 playedAt: buildDateCompare(getTemporallyCloseDateCompareOp(play)), 640 640 }; ··· 692 692 with: qWith 693 693 } = opts; 694 694 695 - let query: FindMany<'plays'> = {}; 695 + const query: FindMany<'plays'> = {}; 696 696 697 - let where: FindWhere<'plays'> = { 697 + const where: FindWhere<'plays'> = { 698 698 componentId, 699 699 playedAt: buildDateCompare(getTemporallyCloseDateCompareOp(play, {bufferTime})), 700 700 }; ··· 790 790 // old way 791 791 // let where: Parameters<(ReturnType<typeof getDb>)['query']['plays']['findMany']>[0]['where'] = { 792 792 // }; 793 - let where: FindWhere<'plays'> = { 793 + const where: FindWhere<'plays'> = { 794 794 componentId: args.componentId 795 795 }; 796 796 if (args.state !== undefined) { ··· 826 826 } 827 827 // so that we can use this type 828 828 // or else assigning an array to OR using only `typeof where.queueStates` causes a type error 829 - let queueWhere: typeof where.queueStates.OR[0][] = []; 829 + const queueWhere: typeof where.queueStates.OR[0][] = []; 830 830 for(const q of queues) { 831 831 queueWhere.push( 832 832 { ··· 853 853 OR: [] 854 854 } 855 855 ]; 856 - let textWhere: typeof where.RAW[] = []; 856 + const textWhere: typeof where.RAW[] = []; 857 857 for(const t of args.text) { 858 858 textWhere.push((p) => sql`lower(json_extract(${p.play}, '$.data.track')) LIKE '%' || ${t.toLocaleLowerCase()} || '%'`) 859 859 textWhere.push((p) => sql`lower(json_extract(${p.play}, '$.data.artists')) LIKE '%' || ${t.toLocaleLowerCase()} || '%'`)
+1 -1
src/backend/common/errors/MSErrors.ts
··· 186 186 const msgParts = [ 187 187 message ?? 'Regex(es) did not match the value given.', 188 188 ]; 189 - let regArr = Array.isArray(regex) ? regex : [regex]; 189 + const regArr = Array.isArray(regex) ? regex : [regex]; 190 190 for(const r of regArr) { 191 191 msgParts.push(`Regex: ${r}`) 192 192 }
+2 -2
src/backend/common/vendor/discord/DiscordUtils.ts
··· 13 13 let startTime: number, 14 14 endTime: number; 15 15 16 - let play: PlayObject = data.play; 16 + const play: PlayObject = data.play; 17 17 18 18 const position = data.position ?? data.play.meta?.trackProgressPosition; 19 19 if(position !== undefined && play.data.duration !== undefined) { ··· 29 29 endTime = dayjs().add(play.data.duration - realPosition, 's').unix() * 1000; 30 30 } 31 31 32 - let activityName = capitalize(play.meta?.musicService ?? play.meta?.mediaPlayerName ?? play.meta?.source ?? 'music') 32 + const activityName = capitalize(play.meta?.musicService ?? play.meta?.mediaPlayerName ?? play.meta?.source ?? 'music') 33 33 34 34 // discord requires all fields to be at least 2 characters long or else it returns this error 35 35 //
+2 -2
src/backend/index.ts
··· 52 52 let logger: FoxLogger; 53 53 54 54 let db: DbConcrete; 55 - let dbConnectionsClosed = false; 55 + const dbConnectionsClosed = false; 56 56 57 57 process.on('uncaughtExceptionMonitor', (err, origin) => { 58 58 const appError = new Error(`Uncaught exception is crashing the app! :( Type: ${origin}`, {cause: err}); ··· 219 219 } 220 220 } 221 221 222 - let runRetentionNow = parseBool(process.env.RETENTION_IMMEDIATE, false); 222 + const runRetentionNow = parseBool(process.env.RETENTION_IMMEDIATE, false); 223 223 224 224 const retentionTask = createRetentionCleanupTask(scrobbleSources, scrobbleClients, logger); 225 225 let retentionJobAdded = false;
+5 -5
src/backend/scrobblers/AbstractScrobbleClient.ts
··· 364 364 } = this.transformRules; 365 365 366 366 if(preCompare.length > 0) { 367 - let pcInits: number[] = [0], 367 + const pcInits: number[] = [0], 368 368 pcMaxStagger: number[] = []; 369 369 for(const hook of preCompare) { 370 370 const t = this.transformManager.getTransformerByStage({type: hook.type, name: hook.name}); ··· 375 375 } 376 376 377 377 if(existing.length > 0) { 378 - let eInits: number[] = [0], 378 + const eInits: number[] = [0], 379 379 eMaxStagger: number[] = []; 380 380 for(const hook of existing) { 381 381 const t = this.transformManager.getTransformerByStage({type: hook.type, name: hook.name}); ··· 1449 1449 } 1450 1450 //this.deadLetterScrobbles.splice(index, 1); 1451 1451 this.deadLetterGauge.labels(this.getPrometheusLabels()).dec(); 1452 - let queueUpdate: Partial<QueueStateNew> = { 1452 + const queueUpdate: Partial<QueueStateNew> = { 1453 1453 updatedAt: dayjs(), 1454 1454 queueStatus: 'completed' 1455 1455 } ··· 1807 1807 with: withQuery = ['input','parent-input','queues'], 1808 1808 ...rest 1809 1809 } = args; 1810 - let parsedLimit = limit !== undefined ? Number.parseInt(limit as unknown as string) : undefined; 1811 - let parsedOffset = offset !== undefined ? Number.parseInt(offset as unknown as string) : undefined; 1810 + const parsedLimit = limit !== undefined ? Number.parseInt(limit as unknown as string) : undefined; 1811 + const parsedOffset = offset !== undefined ? Number.parseInt(offset as unknown as string) : undefined; 1812 1812 return this.playRepo.findPlaysPaginated({limit: parsedLimit, offset: parsedOffset, with: withQuery, ...rest}); 1813 1813 } 1814 1814
+1 -1
src/backend/scrobblers/ScrobbleClients.ts
··· 524 524 } 525 525 526 526 getPlayingNow = (source: string, scrobbleTo: string[]): PlayObject[] => { 527 - let playingNow = []; 527 + const playingNow = []; 528 528 for (const client of this.clients) { 529 529 if(!client.supportsNowPlaying || !client.nowPlayingEnabled) { 530 530 continue;
+5 -5
src/backend/sources/AbstractSource.ts
··· 225 225 } = this.transformRules; 226 226 227 227 if (preCompare.length > 0) { 228 - let pcInits: number[] = [0], 228 + const pcInits: number[] = [0], 229 229 pcMaxStagger: number[] = [0]; 230 230 for (const hook of this.transformRules.preCompare) { 231 231 const t = this.transformManager.getTransformerByStage({ type: hook.type, name: hook.name }); ··· 236 236 } 237 237 238 238 if (postCompare.length > 0) { 239 - let postInits: number[] = [0], 239 + const postInits: number[] = [0], 240 240 postMaxStagger: number[] = [0]; 241 241 for (const hook of this.transformRules.postCompare) { 242 242 const t = this.transformManager.getTransformerByStage({ type: hook.type, name: hook.name }); ··· 676 676 this.lastActivityAt = dayjs(); 677 677 let checkCount = 0; 678 678 let checksOverThreshold = 0; 679 - let checkActiveFor = 120; 679 + const checkActiveFor = 120; 680 680 let maxInterval = DEFAULT_POLLING_MAX_INTERVAL; 681 681 682 682 if('maxInterval' in this.config.data) { ··· 835 835 with: withQuery = ['input','parent-input','queues'], 836 836 ...rest 837 837 } = args; 838 - let parsedLimit = limit !== undefined ? Number.parseInt(limit as unknown as string) : undefined; 839 - let parsedOffset = offset !== undefined ? Number.parseInt(offset as unknown as string) : undefined; 838 + const parsedLimit = limit !== undefined ? Number.parseInt(limit as unknown as string) : undefined; 839 + const parsedOffset = offset !== undefined ? Number.parseInt(offset as unknown as string) : undefined; 840 840 return this.playRepo.findPlaysPaginated({limit: parsedLimit, offset: parsedOffset, with: withQuery, ...rest}); 841 841 } 842 842
+2 -2
src/backend/sources/JellyfinApiSource.ts
··· 154 154 this.librariesAllow = parseArrayFromMaybeString(librariesAllow, {lower: true}); 155 155 this.librariesBlock = parseArrayFromMaybeString(librariesBlock, {lower: true}); 156 156 this.allowedLibraryTypes = Array.from(new Set(['music', ...parseArrayFromMaybeString(additionalAllowedLibraryTypes, {lower: true})])); 157 - let mt = parseArrayFromMaybeString(allowMediaTypes, {lower: true}); 157 + const mt = parseArrayFromMaybeString(allowMediaTypes, {lower: true}); 158 158 if(mt.length > 0) { 159 159 this.allowedMediaTypes = []; 160 160 for(const a of allowMediaTypes) { ··· 517 517 const validPlay = this.isActivityValid(sessionData[0], sessionData[1]); 518 518 if(validPlay === true) { 519 519 if(isDebugMode()) { 520 - let stateIdentifyingInfo: string = genGroupIdStr(getPlatformIdFromData(sessionData[0])); 520 + const stateIdentifyingInfo: string = genGroupIdStr(getPlatformIdFromData(sessionData[0])); 521 521 this.logger.trace(`${stateIdentifyingInfo} => Activity Date ${sessionData[1].LastActivityDate} | Playback Checkin: ${sessionData[1].LastPlaybackCheckIn} `) 522 522 } 523 523 validSessions.push(sessionData[0]);
+1 -1
src/backend/sources/PlayerState/AbstractPlayerState.ts
··· 351 351 352 352 const hints: string[] = []; 353 353 354 - let repeatHint = `New Position (${position})`; 354 + const repeatHint = `New Position (${position})`; 355 355 const trackDur = this.currentPlay.data.duration; 356 356 357 357 // new position is close to start of Play
+1 -1
src/backend/sources/PlayerState/JellyfinPlayerState.ts
··· 10 10 } 11 11 12 12 update(state: PlayerStateDataMaybePlay) { 13 - let stat: ReportedPlayerStatus = state.status; 13 + const stat: ReportedPlayerStatus = state.status; 14 14 // if(stat === undefined && state.play?.meta?.event === 'PlaybackProgress') { 15 15 // stat = 'playing'; 16 16 // }
+2 -2
src/backend/sources/SonosSource.ts
··· 206 206 const deviceId = Name === undefined ? NO_DEVICE : `${Name}-${GroupName ?? 'NoGroup'}`; 207 207 208 208 try { 209 - let status = CLIENT_PLAYER_STATE[x.state.transportState] ?? REPORTED_PLAYER_STATUSES.unknown; 209 + const status = CLIENT_PLAYER_STATE[x.state.transportState] ?? REPORTED_PLAYER_STATUSES.unknown; 210 210 211 211 // TODO if status is stopped then drop state if player is also stopped? 212 212 ··· 230 230 } 231 231 } 232 232 233 - let play: PlayObject | undefined = status === REPORTED_PLAYER_STATUSES.stopped || posTrackURI === undefined ? undefined : formatPlayObj(x.state, { device: x.device }); 233 + const play: PlayObject | undefined = status === REPORTED_PLAYER_STATUSES.stopped || posTrackURI === undefined ? undefined : formatPlayObj(x.state, { device: x.device }); 234 234 let playIsEmpty = false; 235 235 236 236 if (play !== undefined && (play.data?.track === undefined && play.data?.artists === undefined)) {
+2 -2
src/backend/tests/config/config.test.ts
··· 57 57 it(`Sample ${componentType}.json parses and validates`, async function () { 58 58 this.timeout(5000); 59 59 60 - let emitter = new EventEmitter(); 60 + const emitter = new EventEmitter(); 61 61 await copyFile(samplePath(componentType), `${componentType}.json`); 62 62 const sources = new ScrobbleSources(emitter, { 63 63 localUrl: new URL('http://example.com'), ··· 89 89 it(`Sample ${componentType}.json parses and validates`, async function () { 90 90 this.timeout(5000); 91 91 92 - let emitter = new EventEmitter(); 92 + const emitter = new EventEmitter(); 93 93 await copyFile(samplePath(componentType), `${componentType}.json`); 94 94 const clients = new ScrobbleClients(emitter, new EventEmitter, { 95 95 localUrl: new URL('http://example.com'),
+19 -19
src/backend/tests/database/drizzle.test.ts
··· 347 347 })); 348 348 const discovered = { 349 349 ...fixtureCreatePlay(), 350 - state: 'discovered' as 'discovered', 350 + state: 'discovered' as const, 351 351 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 352 352 }; 353 353 playData.push(discovered) ··· 370 370 const playData: RepositoryCreatePlayOpts[] = [ 371 371 { 372 372 ...fixtureCreatePlay({ play: generatePlay({ playDate: dayjs().subtract(2, 'm') }) }), 373 - state: 'queued' as 'queued', 373 + state: 'queued' as const, 374 374 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 375 375 }, 376 376 { 377 377 ...fixtureCreatePlay({ play: generatePlay({ playDate: dayjs().subtract(6, 'm') }) }), 378 - state: 'queued' as 'queued', 378 + state: 'queued' as const, 379 379 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 380 380 }, 381 381 { 382 382 ...fixtureCreatePlay({ play: generatePlay({ playDate: dayjs().subtract(8, 'm') }) }), 383 - state: 'queued' as 'queued', 383 + state: 'queued' as const, 384 384 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 385 385 }, 386 386 { 387 387 ...fixtureCreatePlay({ play: generatePlay({ playDate: dayjs().subtract(10, 'm') }) }), 388 - state: 'queued' as 'queued', 388 + state: 'queued' as const, 389 389 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 390 390 }, 391 391 ] ··· 421 421 { 422 422 ...fixtureCreatePlay(), 423 423 componentId: component1[0].id, 424 - state: 'queued' as 'queued', 424 + state: 'queued' as const, 425 425 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 426 426 }, 427 427 { 428 428 ...fixtureCreatePlay(), 429 429 componentId: component3[0].id, 430 - state: 'queued' as 'queued', 430 + state: 'queued' as const, 431 431 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 432 432 }, 433 433 { 434 434 ...fixtureCreatePlay(), 435 435 componentId: component3[0].id, 436 - state: 'queued' as 'queued', 436 + state: 'queued' as const, 437 437 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 438 438 } 439 439 ] ··· 466 466 { 467 467 ...fixtureCreatePlay({play: generatePlay({},{seenAt: dayjs().subtract(25, 'h')})}), 468 468 componentId: component1[0].id, 469 - state: 'queued' as 'queued', 469 + state: 'queued' as const, 470 470 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 471 471 }, 472 472 { 473 473 ...fixtureCreatePlay({play: generatePlay({},{seenAt: dayjs().subtract(26, 'h')})}), 474 474 componentId: component1[0].id, 475 - state: 'queued' as 'queued', 475 + state: 'queued' as const, 476 476 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 477 477 }, 478 478 { 479 479 ...fixtureCreatePlay({play: generatePlay({},{seenAt: dayjs().subtract(26, 'h')})}), 480 480 componentId: component2[0].id, 481 - state: 'queued' as 'queued', 481 + state: 'queued' as const, 482 482 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 483 483 }, 484 484 { 485 485 ...fixtureCreatePlay({play: generatePlay({},{seenAt: dayjs().subtract(25, 'h').subtract(1, 'm')})}), 486 486 componentId: component1[0].id, 487 - state: 'queued' as 'queued', 487 + state: 'queued' as const, 488 488 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 489 489 }, 490 490 ] ··· 496 496 ...fixtureCreatePlay({play: generatePlay({},{seenAt: dayjs().subtract(25, 'h')})}), 497 497 componentId: component2[0].id, 498 498 parentId: initialPlays[1].id, 499 - state: 'queued' as 'queued', 499 + state: 'queued' as const, 500 500 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 501 501 }, 502 502 ]) ··· 593 593 594 594 await withLocalTmpDir(async () => { 595 595 try { 596 - let [db, _] = await getMigratedDb(getDbPath('ms', process.cwd())); 596 + const [db, _] = await getMigratedDb(getDbPath('ms', process.cwd())); 597 597 const stats = await fs.stat(path.resolve('./ms.db')); 598 598 loggerDebug.debug(`Empty => ${stats.size / 1024}kb`); 599 599 } catch (e) { ··· 608 608 609 609 await withLocalTmpDir(async () => { 610 610 try { 611 - let [db, _] = await getMigratedDb(getDbPath('ms', process.cwd())); 611 + const [db, _] = await getMigratedDb(getDbPath('ms', process.cwd())); 612 612 const component = await db.insert(components).values(fixtureCreateComponent()).returning(); 613 613 614 614 const playRepo = new DrizzlePlayRepository(db); ··· 641 641 642 642 await withLocalTmpDir(async () => { 643 643 try { 644 - let [db, _] = await getMigratedDb(getDbPath('ms', process.cwd())); 644 + const [db, _] = await getMigratedDb(getDbPath('ms', process.cwd())); 645 645 const component = await db.insert(components).values(fixtureCreateComponent()).returning(); 646 646 647 647 const playRepo = new DrizzlePlayRepository(db); ··· 674 674 675 675 await withLocalTmpDir(async () => { 676 676 try { 677 - let [db, _] = await getMigratedDb(getDbPath('ms', process.cwd())); 677 + const [db, _] = await getMigratedDb(getDbPath('ms', process.cwd())); 678 678 const component = await db.insert(components).values(fixtureCreateComponent()).returning(); 679 679 680 680 const playRepo = new DrizzlePlayRepository(db); ··· 731 731 { 732 732 ...fixtureCreatePlay(), 733 733 componentId: component1[0].id, 734 - state: 'queued' as 'queued', 734 + state: 'queued' as const, 735 735 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 736 736 }, 737 737 { 738 738 ...fixtureCreatePlay(), 739 739 componentId: component1[0].id, 740 - state: 'queued' as 'queued', 740 + state: 'queued' as const, 741 741 input: { data: generateRandomObj(undefined, { allowUndefined: false }) } 742 742 } 743 743 ]
+2 -2
src/backend/utils/DataUtils.ts
··· 99 99 100 100 const newObj = new Proxy<LowercaseKeys<T>>({} as LowercaseKeys<T>, handler); 101 101 // traverse the Original object converting string keys to upper case 102 - for (var key in obj) { 102 + for (const key in obj) { 103 103 if (typeof key == "string") { 104 - var objKey = key.toUpperCase(); 104 + const objKey = key.toUpperCase(); 105 105 if (!(key in newObj)) 106 106 newObj[objKey] = checkAtomic(obj[key]); 107 107 }
+2 -2
src/backend/utils/ListenFetchUtils.ts
··· 26 26 let plays: PlayObject[] = []; 27 27 requestCount = 0; 28 28 let more = true; 29 - let currOpts = { ...opts }; 29 + const currOpts = { ...opts }; 30 30 let initial = true; 31 31 while (more) { 32 32 requestCount++; ··· 110 110 let plays: PlayObject[] = []; 111 111 requestCount = 0; 112 112 let more = true; 113 - let currOpts: PaginatedListensTimeRangeOptions = opts; 113 + const currOpts: PaginatedListensTimeRangeOptions = opts; 114 114 let initial = true; 115 115 let timeRangeHint: string; 116 116 if(currOpts.to !== undefined && currOpts.from !== undefined) {
+3 -3
src/backend/utils/NetworkUtils.ts
··· 231 231 } 232 232 233 233 export const formatWebsocketClose = (e: CloseEvent): string => { 234 - let closeParts = []; 234 + const closeParts = []; 235 235 let code = `${e.code}`; 236 236 const codeHint = WEBSOCKET_CLOSE_CODE_REASONS[e.code]; 237 237 if(codeHint !== undefined) { ··· 334 334 logger.trace(`Finished download!`); 335 335 336 336 // Concat the chunks into a single array 337 - let body = new Uint8Array(received); 337 + const body = new Uint8Array(received); 338 338 let position = 0; 339 339 340 340 // Order the chunks by their respective position 341 - for (let chunk of chunks) { 341 + for (const chunk of chunks) { 342 342 body.set(chunk, position); 343 343 position += chunk.length; 344 344 }
+1 -1
src/backend/utils/PlayComparisonUtils.ts
··· 586 586 closestScrobbleParts.push(`Closest Scrobble: ${buildTrackString(result.closestMatchedPlay, scoreTrackOpts)}`); 587 587 } 588 588 closestScrobbleParts.push(result.reason); 589 - let summaryStart = `${capitalize(playObj.meta.source ?? 'Source')}: ${buildTrackString(playObj, scoreTrackOpts)} => ${closestScrobbleParts.join(' => ')}`; 589 + const summaryStart = `${capitalize(playObj.meta.source ?? 'Source')}: ${buildTrackString(playObj, scoreTrackOpts)} => ${closestScrobbleParts.join(' => ')}`; 590 590 const summary = `${summaryStart}${result.breakdowns.length > 0 ? `\n${result.breakdowns.join('\n')}` : ''}` 591 591 result.summary = summary; 592 592 if(log) {
+3 -3
src/backend/utils/TimeUtils.ts
··· 345 345 hintPrefix = true 346 346 } = thresholds; 347 347 348 - let hintStart = hintPrefix ? `Position (${position}) ` : ''; 348 + const hintStart = hintPrefix ? `Position (${position}) ` : ''; 349 349 const trackDur = play.data.duration; 350 350 const closeStartNum = position <= absolute; 351 351 const hints: string[] = []; ··· 371 371 hintPrefix = true 372 372 } = thresholds; 373 373 374 - let hintStart = hintPrefix ? `Position (${position}) ` : ''; 374 + const hintStart = hintPrefix ? `Position (${position}) ` : ''; 375 375 const trackDur = play.data.duration; 376 376 377 377 if(trackDur === undefined) { ··· 397 397 hintPrefix = true 398 398 } = thresholds; 399 399 400 - let hintStart = hintPrefix ? `Duration listened (${duration}s) ` : ''; 400 + const hintStart = hintPrefix ? `Duration listened (${duration}s) ` : ''; 401 401 const trackDur = play.data.duration; 402 402 const absPlayed = duration >= absolute; 403 403 const hints: string[] = [];
+1 -1
src/client/components/ErrorAlert.tsx
··· 30 30 } 31 31 const errorIdentifier = `${data.name ?? ''}${data.code !== undefined ? ` (${data.code}) ` : ''}`; 32 32 const errorElm = errorIdentifier === '' ? null : <Code variant="surface" mx="1">{errorIdentifier}</Code> 33 - let messageElm: React.JSX.Element = ( 33 + const messageElm: React.JSX.Element = ( 34 34 <HStack {...containerProps}> 35 35 {cause ? <FatArrowRight/> : null} 36 36 <Text {...textProps}>{cause ? <Span fontWeight="semibold">Caused By: </Span> : ''}{errorElm}{data.message}</Text>
+1 -1
src/client/components/MobileMenu.tsx
··· 14 14 export const MobileSidebarNav = (props: { hideFrom?: BreakpointName | false } = {}) => { 15 15 const [isOpen, setIsOpen] = useState(false); 16 16 17 - let location = useLocation(); 17 + const location = useLocation(); 18 18 19 19 const closeMenu = () => setIsOpen(false) 20 20
+2 -2
src/client/components/TransformSteps.tsx
··· 19 19 20 20 export const diffElements = (original: JsonPlayObject, steps: LifecycleStep[]): [React.JSX.Element[], JsonPlayObject?] => { 21 21 22 - let currentPlay: JsonPlayObject = structuredClone(original); // JSON.parse(JSON.stringify(original)); 22 + const currentPlay: JsonPlayObject = structuredClone(original); // JSON.parse(JSON.stringify(original)); 23 23 let patchFailed = false; 24 24 25 25 const diffElements: React.JSX.Element[] | null = []; ··· 45 45 diffElements.push(<ChakraCodeBlockShort key={`diffblockfallback-${index}`} title="Diff Patch" code={patch} />); 46 46 continue; 47 47 } 48 - let left: JsonPlayObject = structuredClone(currentPlay); // JSON.parse(JSON.stringify(currentPlay)); 48 + const left: JsonPlayObject = structuredClone(currentPlay); // JSON.parse(JSON.stringify(currentPlay)); 49 49 left.data.meta = { 50 50 ...(left.data.meta ?? {}), 51 51 brainz: {
+1 -1
src/client/components/diffs/JsonDiff.tsx
··· 63 63 const { 64 64 name = 'Content' 65 65 } = fileContentOpts; 66 - let fileName: string = name; 66 + const fileName: string = name; 67 67 let lang: FileContents['lang']; 68 68 if(typeof val === 'string') { 69 69 strContent = val;
+1 -1
src/client/components/msComponent/MSComponentDetailed.tsx
··· 93 93 let primaryAction: React.JSX.Element | undefined; 94 94 let menuElm: React.JSX.Element | undefined; 95 95 let menuItems: React.JSX.Element[] = []; 96 - let badgeProps: BadgeProps = {}; 96 + const badgeProps: BadgeProps = {}; 97 97 switch(props.data.state) { 98 98 case COMPONENT_STATE.RUNNING: 99 99 primaryAction = <PowerOffButton {...primaryActionProps}/>
+1 -1
src/client/components/msComponent/MSComponentSummary.tsx
··· 29 29 let sleepingRender: React.JSX.Element = null; 30 30 31 31 let body = <Card.Footer/>; 32 - let cardHeaderProps: Card.HeaderProps = {}; 32 + const cardHeaderProps: Card.HeaderProps = {}; 33 33 const isClient = isComponentClientApiJson(data); 34 34 if(isComponentSourceApiJson(data)) { 35 35 const {
+2 -2
src/client/components/player/PlayerTimestamp.tsx
··· 8 8 } 9 9 10 10 const convertTime = (rawTime: number) => { 11 - let mins = Math.floor(rawTime / 60); 12 - let seconds = rawTime - (mins * 60); 11 + const mins = Math.floor(rawTime / 60); 12 + const seconds = rawTime - (mins * 60); 13 13 let secStr: string = seconds.toString(); 14 14 if (seconds < 10) { 15 15 secStr = "0" + seconds;
+1 -1
src/client/deadLetter/DeadPage.tsx
··· 30 30 data = [], 31 31 } = props; 32 32 33 - let [searchParams, setSearchParams] = useSearchParams(); 33 + const [searchParams, setSearchParams] = useSearchParams(); 34 34 const { 35 35 isLoading, 36 36 isSuccess
+1 -1
src/client/recent/RecentPage.tsx
··· 32 32 </div>; 33 33 34 34 const recent = () => { 35 - let [searchParams, setSearchParams] = useSearchParams(); 35 + const [searchParams, setSearchParams] = useSearchParams(); 36 36 const { 37 37 data = [], 38 38 error,
+1 -1
src/client/scrobbled/ScrobbledPage.tsx
··· 14 14 } 15 15 16 16 const scrobbled = () => { 17 - let [searchParams, setSearchParams] = useSearchParams(); 17 + const [searchParams, setSearchParams] = useSearchParams(); 18 18 const { 19 19 data = [], 20 20 error,
+1 -1
src/client/utils/index.tsx
··· 54 54 return 'N/A'; 55 55 } 56 56 57 - let parts = []; 57 + const parts = []; 58 58 59 59 const date = dayjs(iso); 60 60 let useDate = includeDate;
+2 -2
src/core/DataUtils.ts
··· 129 129 * @see https://stackoverflow.com/a/20798567/1469797 130 130 */ 131 131 export const getAllIndexes = <T>(arr: T[], truthyFunc: (val: T) => boolean) => { 132 - var indexes = [], i: number; 133 - for(i = 0; i < arr.length; i++) 132 + const indexes = []; 133 + for(let i = 0; i < arr.length; i++) 134 134 if (truthyFunc(arr[i])) 135 135 indexes.push(i); 136 136 return indexes;
+1 -1
src/core/StringUtils.ts
··· 143 143 expandMeta = false 144 144 } = options || {}; 145 145 146 - let meta: string[] = []; 146 + const meta: string[] = []; 147 147 if(play.meta !== undefined) { 148 148 for(const [metaType,metaObject] of Object.entries(play.meta)) { 149 149 for(const [metaKey, metaValue] of Object.entries(metaObject)) {
+2 -3
src/core/tests/utils/PlayTestUtils.ts
··· 147 147 } 148 148 149 149 export const generatePlayerStateData = (options: Omit<PlayerStateDataMaybePlay, 'platformId'> & {playData?: ObjectPlayData, playMeta?: PlayMeta, platformId?: PlayPlatformId} = {}): PlayerStateDataMaybePlay => { 150 - let play: PlayObject = options.play ?? generatePlay(options.playData, options.playMeta); 150 + const play: PlayObject = options.play ?? generatePlay(options.playData, options.playMeta); 151 151 if(options.position !== undefined) { 152 152 play.meta.trackProgressPosition = options.position; 153 153 } ··· 516 516 } 517 517 518 518 export const generateSourcePlayerObj = (opts: GenerateSourcePlayerObjOptions): SourcePlayerObj => { 519 - let play: PlayObject; 520 519 let platformId: string; 521 520 if(opts.play !== undefined) { 522 521 platformId = opts.play.meta.deviceId; 523 522 } else { 524 523 platformId = opts.playPlatform ?? genGroupIdStr(SINGLE_USER_PLATFORM_ID); 525 524 } 526 - play = opts.play ?? generatePlay(opts.playOpts, {deviceId:platformId}); 525 + const play: PlayObject = opts.play ?? generatePlay(opts.playOpts, {deviceId:platformId}); 527 526 528 527 const { 529 528 status = {},