add prettier config

This commit is contained in:
Lee 2023-10-17 23:41:42 +01:00
parent f6f56aa09c
commit 27f3c4e030
75 changed files with 549 additions and 543 deletions

6
.prettierrc.json Normal file

@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": false
}

@ -26,7 +26,7 @@
(u = 0), (u = 0),
(i = 0); (i = 0);
}, },
!1, !1
), ),
e.addEventListener( e.addEventListener(
"touchmove", "touchmove",
@ -36,7 +36,7 @@
r = t.touches[0].clientY; r = t.touches[0].clientY;
(u = n - e), (i = a - r); (u = n - e), (i = a - r);
}, },
!1, !1
), ),
e.addEventListener( e.addEventListener(
"touchend", "touchend",
@ -67,15 +67,15 @@
bubbles: !0, bubbles: !0,
cancelable: !0, cancelable: !0,
detail: b, detail: b,
}), })
), ),
s.dispatchEvent( s.dispatchEvent(
new CustomEvent(d, { bubbles: !0, cancelable: !0, detail: b }), new CustomEvent(d, { bubbles: !0, cancelable: !0, detail: b })
); );
} }
(n = null), (a = null), (r = null); (n = null), (a = null), (r = null);
}, },
!1, !1
); );
var n = null, var n = null,
a = null, a = null,

@ -20,7 +20,7 @@ fs.writeFileSync(
buildDate: buildDate:
new Date().toISOString().substr(0, 19).replace("T", " ") + " UTC", new Date().toISOString().substr(0, 19).replace("T", " ") + " UTC",
buildVersion, buildVersion,
}), })
); );
function serve() { function serve() {
@ -39,7 +39,7 @@ function serve() {
{ {
stdio: ["ignore", "inherit", "inherit"], stdio: ["ignore", "inherit", "inherit"],
shell: true, shell: true,
}, }
); );
process.on("SIGTERM", toExit); process.on("SIGTERM", toExit);
@ -104,7 +104,7 @@ export default [
fs.copyFileSync( fs.copyFileSync(
path.resolve("./node_modules/comlink/dist/umd/comlink.min.js"), path.resolve("./node_modules/comlink/dist/umd/comlink.min.js"),
path.resolve("./public/build/comlink.min.js"), path.resolve("./public/build/comlink.min.js")
); );
}, },
}, },
@ -150,7 +150,7 @@ export default [
fs.copyFileSync( fs.copyFileSync(
path.resolve("./src/workers/stats-worker.js"), path.resolve("./src/workers/stats-worker.js"),
path.resolve("./public/build/stats-worker.js"), path.resolve("./public/build/stats-worker.js")
); );
}, },
}, },

@ -22,7 +22,7 @@ export const onLegendClick = (event, legendItem, legend) => {
(ci?.config?.data?.datasets?.[a?.datasetIndex]?.axisOrder ?? (ci?.config?.data?.datasets?.[a?.datasetIndex]?.axisOrder ??
a?.datasetIndex) - a?.datasetIndex) -
(ci?.config?.data?.datasets?.[b?.datasetIndex]?.axisOrder ?? (ci?.config?.data?.datasets?.[b?.datasetIndex]?.axisOrder ??
b?.datasetIndex), b?.datasetIndex)
) )
.reduce( .reduce(
(cum, legendItem) => { (cum, legendItem) => {
@ -44,7 +44,7 @@ export const onLegendClick = (event, legendItem, legend) => {
return cum; return cum;
}, },
{ first: null, second: null }, { first: null, second: null }
); );
Object.keys(yAxes).forEach((currentAxisKey) => { Object.keys(yAxes).forEach((currentAxisKey) => {

@ -39,7 +39,7 @@ export default {
: left + 3, : left + 3,
region?.position?.vertical === "bottom" region?.position?.vertical === "bottom"
? top + 2 ? top + 2
: top - fontSize - 1, : top - fontSize - 1
); );
} }
} }

@ -70,7 +70,7 @@ function updateScoresStats(playerData, playerStats) {
bgColor: "var(--ppColour)", bgColor: "var(--ppColour)",
}, },
] ]
: [], : []
) )
.filter((s) => s && (!playerStats || s.label !== "Average")); .filter((s) => s && (!playerStats || s.label !== "Average"));
} }

@ -74,7 +74,7 @@ export default () => {
order: "desc", order: "desc",
page: paramsArr[2] ?? serviceDefaultParams?.page, page: paramsArr[2] ?? serviceDefaultParams?.page,
}, },
service, service
); );
case "accsaber": case "accsaber":
@ -88,7 +88,7 @@ export default () => {
: "desc", : "desc",
page: paramsArr[3] ?? serviceDefaultParams?.page, page: paramsArr[3] ?? serviceDefaultParams?.page,
}, },
service, service
); );
case "scoresaber": case "scoresaber":
@ -102,7 +102,7 @@ export default () => {
: "desc", : "desc",
page: paramsArr[2] ?? serviceDefaultParams?.page, page: paramsArr[2] ?? serviceDefaultParams?.page,
}, },
service, service
); );
} }
}; };

@ -5,19 +5,19 @@ export default (name, getObjKey) => {
// update data cached on another node // update data cached on another node
eventBus.on("cache-key-set-" + name, ({ key, value }, isLocal) => eventBus.on("cache-key-set-" + name, ({ key, value }, isLocal) =>
!isLocal ? set(key, value, false) : null, !isLocal ? set(key, value, false) : null
); );
eventBus.on("cache-all-set" + name, ({ data }, isLocal) => eventBus.on("cache-all-set" + name, ({ data }, isLocal) =>
!isLocal ? setAll(data, false) : null, !isLocal ? setAll(data, false) : null
); );
eventBus.on("cache-merge-" + name, ({ data }, isLocal) => eventBus.on("cache-merge-" + name, ({ data }, isLocal) =>
!isLocal ? merge(data, false) : null, !isLocal ? merge(data, false) : null
); );
eventBus.on("cache-key-forget-" + name, ({ key }, isLocal) => eventBus.on("cache-key-forget-" + name, ({ key }, isLocal) =>
!isLocal ? forget(key, false) : null, !isLocal ? forget(key, false) : null
); );
eventBus.on("cache-flush-" + name, (_, isLocal) => eventBus.on("cache-flush-" + name, (_, isLocal) =>
!isLocal ? flush(false) : null, !isLocal ? flush(false) : null
); );
const set = (key, value, emitEvent = true) => { const set = (key, value, emitEvent = true) => {

@ -27,7 +27,7 @@ async function openDatabase() {
db = await openDB("ssr", SSR_DB_VERSION, { db = await openDB("ssr", SSR_DB_VERSION, {
async upgrade(db, oldVersion, newVersion, transaction) { async upgrade(db, oldVersion, newVersion, transaction) {
log.info( log.info(
`Converting database from version ${oldVersion} to version ${newVersion}`, `Converting database from version ${oldVersion} to version ${newVersion}`
); );
dbNewVersion = newVersion; dbNewVersion = newVersion;
@ -50,7 +50,7 @@ async function openDatabase() {
playersHistory.createIndex( playersHistory.createIndex(
"players-history-timestamp", "players-history-timestamp",
"timestamp", "timestamp",
{ unique: false }, { unique: false }
); );
const scoresStore = db.createObjectStore("scores", { const scoresStore = db.createObjectStore("scores", {
@ -89,17 +89,17 @@ async function openDatabase() {
{ {
keyPath: "_idbId", keyPath: "_idbId",
autoIncrement: true, autoIncrement: true,
}, }
); );
rankedsChangesStore.createIndex( rankedsChangesStore.createIndex(
"rankeds-changes-timestamp", "rankeds-changes-timestamp",
"timestamp", "timestamp",
{ unique: false }, { unique: false }
); );
rankedsChangesStore.createIndex( rankedsChangesStore.createIndex(
"rankeds-changes-leaderboardId", "rankeds-changes-leaderboardId",
"leaderboardId", "leaderboardId",
{ unique: false }, { unique: false }
); );
// no autoIncrement, no keyPath - key must be provided // no autoIncrement, no keyPath - key must be provided
@ -197,12 +197,12 @@ async function openDatabase() {
{ {
keyPath: "id", keyPath: "id",
autoIncrement: false, autoIncrement: false,
}, }
); );
scoresUpdateQueue.createIndex( scoresUpdateQueue.createIndex(
"scores-update-queue-fetchedAt", "scores-update-queue-fetchedAt",
"fetchedAt", "fetchedAt",
{ unique: false }, { unique: false }
); );
case newVersion >= 8 && oldVersion <= 7: case newVersion >= 8 && oldVersion <= 7:
@ -220,7 +220,7 @@ async function openDatabase() {
playersHistoryStorev9.createIndex( playersHistoryStorev9.createIndex(
"players-history-playerIdSsTimestamp", "players-history-playerIdSsTimestamp",
"playerIdSsTimestamp", "playerIdSsTimestamp",
{ unique: true }, { unique: true }
); );
// NO break here // NO break here
@ -246,17 +246,17 @@ async function openDatabase() {
{ {
keyPath: "id", keyPath: "id",
autoIncrement: false, autoIncrement: false,
}, }
); );
accSaberPlayersStore.createIndex( accSaberPlayersStore.createIndex(
"accsaber-players-playerId", "accsaber-players-playerId",
"playerId", "playerId",
{ unique: false }, { unique: false }
); );
accSaberPlayersStore.createIndex( accSaberPlayersStore.createIndex(
"accsaber-players-category", "accsaber-players-category",
"category", "category",
{ unique: false }, { unique: false }
); );
// NO break here // NO break here
@ -267,12 +267,12 @@ async function openDatabase() {
{ {
keyPath: "playerIdTimestamp", keyPath: "playerIdTimestamp",
autoIncrement: false, autoIncrement: false,
}, }
); );
accSaberPlayersHistoryStore.createIndex( accSaberPlayersHistoryStore.createIndex(
"accsaber-players-history-playerId", "accsaber-players-history-playerId",
"playerId", "playerId",
{ unique: false }, { unique: false }
); );
// NO break here // NO break here
@ -307,7 +307,7 @@ async function openDatabase() {
objectStores, objectStores,
closure, closure,
mode = "readwrite", mode = "readwrite",
options = { durability: "strict" }, options = { durability: "strict" }
) => { ) => {
try { try {
const tx = db.transaction(objectStores, mode, options); const tx = db.transaction(objectStores, mode, options);

@ -41,7 +41,7 @@ const allFixes = {
: []; : [];
allAppliedFixes.push(fixName); allAppliedFixes.push(fixName);
await keyValueStore.put(allAppliedFixes, FIXES_KEY); await keyValueStore.put(allAppliedFixes, FIXES_KEY);
}, }
); );
}, },
}, },
@ -75,14 +75,14 @@ const allFixes = {
log.info( log.info(
`Unable to convert, deleting a song`, `Unable to convert, deleting a song`,
"DBFix", "DBFix",
beatSaverSong, beatSaverSong
); );
} }
} else { } else {
log.info( log.info(
`No metadata characteristics, skipping a song`, `No metadata characteristics, skipping a song`,
"DBFix", "DBFix",
beatSaverSong, beatSaverSong
); );
} }
@ -99,7 +99,7 @@ const allFixes = {
await keyValueStore.put(allAppliedFixes, FIXES_KEY); await keyValueStore.put(allAppliedFixes, FIXES_KEY);
log.info(`${songCount} BeatSaver song(s) converted`, "DBFix"); log.info(`${songCount} BeatSaver song(s) converted`, "DBFix");
}, }
); );
}, },
}, },
@ -150,8 +150,8 @@ const allFixes = {
lastUpdated: null, lastUpdated: null,
login: twitchLogin, login: twitchLogin,
playerId, playerId,
}), })
), )
); );
await addAppliedFix(fixName); await addAppliedFix(fixName);
@ -203,7 +203,7 @@ const allFixes = {
: []; : [];
allAppliedFixes.push(fixName); allAppliedFixes.push(fixName);
await keyValueStore.put(allAppliedFixes, FIXES_KEY); await keyValueStore.put(allAppliedFixes, FIXES_KEY);
}, }
); );
}, },
}, },
@ -216,7 +216,7 @@ export default async () => {
const neededFixes = Object.keys(allFixes).filter( const neededFixes = Object.keys(allFixes).filter(
(f) => (f) =>
!appliedFixes.includes(f) && !appliedFixes.includes(f) &&
(!allFixes[f].validTo || allFixes[f].validTo > new Date()), (!allFixes[f].validTo || allFixes[f].validTo > new Date())
); );
if (!neededFixes.length) return; if (!neededFixes.length) return;

@ -75,7 +75,7 @@ export default (storeName, inlineKeyName = undefined, indexesKeyNames = {}) => {
const cacheKey = getCacheKeyFor(key); const cacheKey = getCacheKeyFor(key);
return repositoryCache.get(key, () => return repositoryCache.get(key, () =>
resolvePromiseOrWaitForPending(cacheKey, () => db.get(storeName, key)), resolvePromiseOrWaitForPending(cacheKey, () => db.get(storeName, key))
); );
}; };
@ -89,7 +89,7 @@ export default (storeName, inlineKeyName = undefined, indexesKeyNames = {}) => {
const getFromDb = () => const getFromDb = () =>
resolvePromiseOrWaitForPending(cacheKey, () => resolvePromiseOrWaitForPending(cacheKey, () =>
db.getFromIndex(storeName, indexName, query), db.getFromIndex(storeName, indexName, query)
); );
if (query && query instanceof IDBKeyRange) return getFromDb(); if (query && query instanceof IDBKeyRange) return getFromDb();
@ -114,7 +114,7 @@ export default (storeName, inlineKeyName = undefined, indexesKeyNames = {}) => {
isIndexDataAvailable(cacheKey) || isIndexDataAvailable(cacheKey) ||
isIndexDataAvailable(fullIndexCacheKey) isIndexDataAvailable(fullIndexCacheKey)
? filterItems ? filterItems
: null, : null
); );
}; };
@ -134,7 +134,7 @@ export default (storeName, inlineKeyName = undefined, indexesKeyNames = {}) => {
const data = convertArrayToObjectByKey(await getFromDb(), inlineKeyName); const data = convertArrayToObjectByKey(await getFromDb(), inlineKeyName);
const ret = Object.values(repositoryCache.setAll(data)).filter( const ret = Object.values(repositoryCache.setAll(data)).filter(
filterUndefined, filterUndefined
); );
setAllDataAvailabilityStatus(); setAllDataAvailabilityStatus();
@ -148,7 +148,7 @@ export default (storeName, inlineKeyName = undefined, indexesKeyNames = {}) => {
const getAllFromIndex = async ( const getAllFromIndex = async (
indexName, indexName,
query = undefined, query = undefined,
refreshCache = false, refreshCache = false
) => { ) => {
if (hasOutOfLineKey()) if (hasOutOfLineKey())
throw `getAllFromIndex() is not available for stores with out-of-line key`; throw `getAllFromIndex() is not available for stores with out-of-line key`;
@ -159,7 +159,7 @@ export default (storeName, inlineKeyName = undefined, indexesKeyNames = {}) => {
const getFromDb = async () => const getFromDb = async () =>
resolvePromiseOrWaitForPending(cacheKey, () => resolvePromiseOrWaitForPending(cacheKey, () =>
db.getAllFromIndex(storeName, indexName, query), db.getAllFromIndex(storeName, indexName, query)
); );
if (query && query instanceof IDBKeyRange) return getFromDb(); if (query && query instanceof IDBKeyRange) return getFromDb();

@ -62,7 +62,7 @@ let app = null;
"InvalidStateError: A mutation operation was attempted on a database that did not allow mutations." "InvalidStateError: A mutation operation was attempted on a database that did not allow mutations."
) )
error = new Error( error = new Error(
"Firefox in private mode does not support the database. Please run the site in normal mode.", "Firefox in private mode does not support the database. Please run the site in normal mode."
); );
app = new ErrorComponent({ app = new ErrorComponent({

@ -24,7 +24,7 @@ export default (size = DEFAULT_CACHE_SIZE, expiryIn = MINUTE) => {
cum[key] = value; cum[key] = value;
return cum; return cum;
}, },
{}, {}
); );
} }
@ -43,7 +43,7 @@ export default (size = DEFAULT_CACHE_SIZE, expiryIn = MINUTE) => {
if (value.headers) { if (value.headers) {
const headers = new Headers(); const headers = new Headers();
Object.keys(value.headers).map((k) => Object.keys(value.headers).map((k) =>
headers.append(k, value.headers[k]), headers.append(k, value.headers[k])
); );
newValue.headers = headers; newValue.headers = headers;
} }

@ -30,7 +30,7 @@ const get = async ({
const response = await queue.ACCSABER.playerRankHistory( const response = await queue.ACCSABER.playerRankHistory(
playerId, playerId,
priority, priority,
queueOptions, queueOptions
); );
return { return {

@ -23,7 +23,7 @@ const get = async ({
category, category,
page, page,
priority, priority,
queueOptions, queueOptions
); );
return { return {

@ -85,7 +85,7 @@ const get = async ({
playerId, playerId,
page, page,
priority, priority,
queueOptions, queueOptions
); );
return { return {

@ -23,7 +23,7 @@ export default (get, process) => {
const response = await clientGet({ ...getOptions, priority, fullResponse }); const response = await clientGet({ ...getOptions, priority, fullResponse });
const processedResponse = process( const processedResponse = process(
fullResponse ? getResponseBody(response) : response, fullResponse ? getResponseBody(response) : response
); );
return fullResponse return fullResponse

@ -56,7 +56,7 @@ const get = async ({
leaderboardId, leaderboardId,
page, page,
priority, priority,
queueOptions, queueOptions
); );
const client = createClient(get, process); const client = createClient(get, process);

@ -5,7 +5,7 @@ import createClient from "../../generic";
const process = (response) => { const process = (response) => {
const apiProcessedResponse = api.process( const apiProcessedResponse = api.process(
response && response.player ? response.player : null, response && response.player ? response.player : null
); );
if (!opt(apiProcessedResponse, "player.playerInfo")) return null; if (!opt(apiProcessedResponse, "player.playerInfo")) return null;
@ -20,7 +20,7 @@ const process = (response) => {
const externalProfileUrl = opt( const externalProfileUrl = opt(
response, response,
"player.playerInfo.externalProfileUrl", "player.playerInfo.externalProfileUrl"
); );
if (externalProfileUrl) { if (externalProfileUrl) {
apiProcessedResponse.playerInfo.externalProfileUrl = externalProfileUrl; apiProcessedResponse.playerInfo.externalProfileUrl = externalProfileUrl;

@ -27,7 +27,7 @@ export default (dlManager) => {
if (!playerId) return; if (!playerId) return;
await playerService.remove(playerId, purgeScores); await playerService.remove(playerId, purgeScores);
}, }
); );
eventBus.on("dl-manager-pause-cmd", () => { eventBus.on("dl-manager-pause-cmd", () => {

@ -39,7 +39,7 @@ const enqueue = async (
type, type,
force = false, force = false,
data = null, data = null,
then = null, then = null
) => { ) => {
if (!type || !type.name || !Number.isFinite(type.priority)) { if (!type || !type.name || !Number.isFinite(type.priority)) {
log.warn(`Unknown type enqueued.`, "DlManager", type); log.warn(`Unknown type enqueued.`, "DlManager", type);
@ -49,9 +49,9 @@ const enqueue = async (
log.debug( log.debug(
`Try to enqueue type ${type.name}. Forced: ${force}, data: ${JSON.stringify( `Try to enqueue type ${type.name}. Forced: ${force}, data: ${JSON.stringify(
data, data
)}`, )}`,
"DlManager", "DlManager"
); );
const priority = force ? PRIORITY.HIGHEST : type.priority; const priority = force ? PRIORITY.HIGHEST : type.priority;
@ -78,13 +78,13 @@ const enqueue = async (
queue, queue,
{ ...TYPES.ACTIVE_PLAYERS, priority: PRIORITY.HIGHEST }, { ...TYPES.ACTIVE_PLAYERS, priority: PRIORITY.HIGHEST },
force, force,
{ playerId: mainPlayerId }, { playerId: mainPlayerId }
), ),
enqueue( enqueue(
queue, queue,
{ ...TYPES.PLAYER_SCORES, priority: PRIORITY.HIGHEST }, { ...TYPES.PLAYER_SCORES, priority: PRIORITY.HIGHEST },
force, force,
{ playerId: mainPlayerId }, { playerId: mainPlayerId }
), ),
]); ]);
} }
@ -98,9 +98,9 @@ const enqueue = async (
processThen( processThen(
queue.add( queue.add(
async () => rankedsStore.refresh(force, networkPriority), async () => rankedsStore.refresh(force, networkPriority),
priority, priority
), ),
then, then
).then((_) => log.debug("Enqueued rankeds processed.", "DlManager")); ).then((_) => log.debug("Enqueued rankeds processed.", "DlManager"));
break; break;
@ -112,32 +112,32 @@ const enqueue = async (
processThen( processThen(
queue.add( queue.add(
async () => playerService.add(data.playerId, networkPriority), async () => playerService.add(data.playerId, networkPriority),
priority, priority
), ),
then, then
).then((_) => ).then((_) =>
log.debug("Enqueued active players processed.", "DlManager"), log.debug("Enqueued active players processed.", "DlManager")
); );
else else
processThen( processThen(
queue.add( queue.add(
async () => async () =>
playerService.refresh(data.playerId, force, networkPriority), playerService.refresh(data.playerId, force, networkPriority),
priority, priority
), ),
then, then
).then((_) => ).then((_) =>
log.debug("Enqueued active players processed.", "DlManager"), log.debug("Enqueued active players processed.", "DlManager")
); );
} else } else
processThen( processThen(
queue.add( queue.add(
async () => playerService.refreshAll(force, networkPriority), async () => playerService.refreshAll(force, networkPriority),
priority, priority
), ),
then, then
).then((_) => ).then((_) =>
log.debug("Enqueued active players processed.", "DlManager"), log.debug("Enqueued active players processed.", "DlManager")
); );
break; break;
@ -149,21 +149,21 @@ const enqueue = async (
queue.add( queue.add(
async () => async () =>
scoresService.refresh(data.playerId, force, networkPriority), scoresService.refresh(data.playerId, force, networkPriority),
priority, priority
), ),
then, then
).then((_) => ).then((_) =>
log.debug("Enqueued players scores processed.", "DlManager"), log.debug("Enqueued players scores processed.", "DlManager")
); );
else else
processThen( processThen(
queue.add( queue.add(
async () => scoresService.refreshAll(force, networkPriority), async () => scoresService.refreshAll(force, networkPriority),
priority, priority
), ),
then, then
).then((_) => ).then((_) =>
log.debug("Enqueued players scores processed.", "DlManager"), log.debug("Enqueued players scores processed.", "DlManager")
); );
break; break;
@ -173,9 +173,9 @@ const enqueue = async (
processThen( processThen(
queue.add( queue.add(
async () => beatSaviorService.refreshAll(force, networkPriority), async () => beatSaviorService.refreshAll(force, networkPriority),
priority, priority
), ),
then, then
).then((_) => log.debug("Enqueued Beat Savior processed.", "DlManager")); ).then((_) => log.debug("Enqueued Beat Savior processed.", "DlManager"));
break; break;
@ -186,14 +186,14 @@ const enqueue = async (
processThen( processThen(
queue.add( queue.add(
async () => scoresService.updateRankAndPpFromTheQueue(), async () => scoresService.updateRankAndPpFromTheQueue(),
priority, priority
), ),
then, then
).then((_) => ).then((_) =>
log.debug( log.debug(
"Enqueued player scores rank & pp updates processed.", "Enqueued player scores rank & pp updates processed.",
"DlManager", "DlManager"
), )
); );
break; break;
@ -203,9 +203,9 @@ const enqueue = async (
processThen( processThen(
queue.add(async () => accSaberService.refreshAll(), priority), queue.add(async () => accSaberService.refreshAll(), priority),
then, then
).then((_) => ).then((_) =>
log.debug("Enqueued AccSaber updates processed.", "DlManager"), log.debug("Enqueued AccSaber updates processed.", "DlManager")
); );
break; break;
@ -271,7 +271,7 @@ export default async () => {
const nodeId = eventBus.getNodeId(); const nodeId = eventBus.getNodeId();
log.info( log.info(
`Node ${nodeId} is a leader, queue processing enabled`, `Node ${nodeId} is a leader, queue processing enabled`,
"DlManager", "DlManager"
); );
await startSyncing(queue); await startSyncing(queue);
@ -284,7 +284,7 @@ export default async () => {
TYPES.ACTIVE_PLAYERS, TYPES.ACTIVE_PLAYERS,
true, true,
{ playerId, add: true }, { playerId, add: true },
async () => enqueue(queue, TYPES.PLAYER_SCORES, true, { playerId }), async () => enqueue(queue, TYPES.PLAYER_SCORES, true, { playerId })
); );
}; };

@ -33,7 +33,7 @@ export class SsrHttpResponseError extends SsrNetworkError {
`HTTP Error Response: ${ `HTTP Error Response: ${
response && response.status ? response.status : "None" response && response.status ? response.status : "None"
} ${response && response.statusText ? response.statusText : ""}`, } ${response && response.statusText ? response.statusText : ""}`,
...args, ...args
); );
this.name = "SsrHttpResponseError"; this.name = "SsrHttpResponseError";

@ -97,12 +97,12 @@ export async function fetchUrl(url, options = {}, cors = true) {
export async function fetchJson( export async function fetchJson(
url, url,
{ cacheTtl = null, maxAge = null, ...restOptions } = {}, { cacheTtl = null, maxAge = null, ...restOptions } = {}
) { ) {
const options = getOptionsWithCacheKey( const options = getOptionsWithCacheKey(
url, url,
{ cacheTtl, maxAge, ...restOptions }, { cacheTtl, maxAge, ...restOptions },
"json", "json"
); );
const { const {
@ -129,7 +129,7 @@ export async function fetchJson(
body, body,
}, },
fetchCacheKey, fetchCacheKey,
fetchCacheTtl, fetchCacheTtl
); );
}) })
.catch((err) => { .catch((err) => {
@ -141,12 +141,12 @@ export async function fetchJson(
export async function fetchHtml( export async function fetchHtml(
url, url,
{ cacheTtl = null, maxAge = null, ...restOptions } = {}, { cacheTtl = null, maxAge = null, ...restOptions } = {}
) { ) {
const options = getOptionsWithCacheKey( const options = getOptionsWithCacheKey(
url, url,
{ cacheTtl, maxAge, ...restOptions }, { cacheTtl, maxAge, ...restOptions },
"json", "json"
); );
const { const {
@ -172,7 +172,7 @@ export async function fetchHtml(
body: new DOMParser().parseFromString(body, "text/html"), body: new DOMParser().parseFromString(body, "text/html"),
}, },
fetchCacheKey, fetchCacheKey,
fetchCacheTtl, fetchCacheTtl
); );
}); });
} }

@ -21,54 +21,54 @@ export default (options = {}) => {
category = "overall", category = "overall",
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchJson( fetchJson(
substituteVars(RANKING_URL, { category, page }), substituteVars(RANKING_URL, { category, page }),
options, options,
priority, priority
); );
const scores = async ( const scores = async (
playerId, playerId,
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchJson( fetchJson(
substituteVars(PLAYER_SCORES_URL, { playerId, page }), substituteVars(PLAYER_SCORES_URL, { playerId, page }),
options, options,
priority, priority
); );
const playerRankHistory = async ( const playerRankHistory = async (
playerId, playerId,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchJson( fetchJson(
substituteVars(PLAYER_RANK_HISTORY, { playerId }), substituteVars(PLAYER_RANK_HISTORY, { playerId }),
options, options,
priority, priority
); );
const leaderboard = async ( const leaderboard = async (
leaderboardId, leaderboardId,
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchJson( fetchJson(
substituteVars(LEADERBOARD_URL, { leaderboardId, page }), substituteVars(LEADERBOARD_URL, { leaderboardId, page }),
options, options,
priority, priority
); );
const leaderboardInfo = async ( const leaderboardInfo = async (
leaderboardId, leaderboardId,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchJson( fetchJson(
substituteVars(LEADERBOARD_INFO_URL, { leaderboardId }), substituteVars(LEADERBOARD_INFO_URL, { leaderboardId }),
options, options,
priority, priority
); );
return { return {

@ -78,7 +78,7 @@ export default (options = {}) => {
fetchFunc, fetchFunc,
url, url,
options, options,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW
) => { ) => {
for (let i = 0; i <= retries; i++) { for (let i = 0; i <= retries; i++) {
try { try {
@ -140,11 +140,11 @@ export default (options = {}) => {
const queuedFetchJson = async (url, options, priority = PRIORITY.FG_LOW) => const queuedFetchJson = async (url, options, priority = PRIORITY.FG_LOW) =>
resolvePromiseOrWaitForPending(url, () => resolvePromiseOrWaitForPending(url, () =>
retriedFetch(fetchJson, url, options, priority), retriedFetch(fetchJson, url, options, priority)
); );
const queuedFetchHtml = async (url, options, priority = PRIORITY.FG_LOW) => const queuedFetchHtml = async (url, options, priority = PRIORITY.FG_LOW) =>
resolvePromiseOrWaitForPending(url, () => resolvePromiseOrWaitForPending(url, () =>
retriedFetch(fetchHtml, url, options, priority), retriedFetch(fetchHtml, url, options, priority)
); );
const getRateLimit = () => currentRateLimit; const getRateLimit = () => currentRateLimit;

@ -63,10 +63,10 @@ const initQueue = (queue) => {
export default { export default {
SCORESABER_API: initQueue( SCORESABER_API: initQueue(
createScoreSaberApiQueue({ concurrency: 3, timeout: 95000 }), createScoreSaberApiQueue({ concurrency: 3, timeout: 95000 })
), ),
SCORESABER_PAGE: initQueue( SCORESABER_PAGE: initQueue(
createScoreSaberPageQueue({ concurrency: 3, timeout: 30000 }), createScoreSaberPageQueue({ concurrency: 3, timeout: 30000 })
), ),
BEATMAPS: initQueue( BEATMAPS: initQueue(
createBeatMapsApiQueue({ createBeatMapsApiQueue({
@ -74,7 +74,7 @@ export default {
timeout: 10000, timeout: 10000,
intervalCap: 10, intervalCap: 10,
interval: 1000, interval: 1000,
}), })
), ),
BEATSAVIOR: initQueue( BEATSAVIOR: initQueue(
createBeatSaviorApiQueue({ createBeatSaviorApiQueue({
@ -82,7 +82,7 @@ export default {
timeout: 10000, timeout: 10000,
intervalCap: 60, intervalCap: 60,
interval: 60000, interval: 60000,
}), })
), ),
TWITCH: initQueue( TWITCH: initQueue(
createTwitchApiQueue({ createTwitchApiQueue({
@ -90,10 +90,10 @@ export default {
timeout: 8000, timeout: 8000,
intervalCap: 800, intervalCap: 800,
interval: 60000, interval: 60000,
}), })
), ),
ACCSABER: initQueue( ACCSABER: initQueue(
createAccSaberApiQueue({ concurrency: 2, timeout: 10000 }), createAccSaberApiQueue({ concurrency: 2, timeout: 10000 })
), ),
PRIORITY, PRIORITY,
}; };

@ -27,7 +27,7 @@ export default (options = {}) => {
playerId, playerId,
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchJson(substituteVars(baseUrl, { playerId, page }), options, priority); fetchJson(substituteVars(baseUrl, { playerId, page }), options, priority);
@ -35,21 +35,21 @@ export default (options = {}) => {
fetchJson( fetchJson(
substituteVars(SS_API_PLAYER_INFO_URL, { playerId }), substituteVars(SS_API_PLAYER_INFO_URL, { playerId }),
options, options,
priority, priority
); );
const recentScores = async ( const recentScores = async (
playerId, playerId,
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => fetchScores(SS_API_RECENT_SCORES_URL, playerId, page, priority, options); ) => fetchScores(SS_API_RECENT_SCORES_URL, playerId, page, priority, options);
const topScores = async ( const topScores = async (
playerId, playerId,
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => fetchScores(SS_API_TOP_SCORES_URL, playerId, page, priority, options); ) => fetchScores(SS_API_TOP_SCORES_URL, playerId, page, priority, options);
const findPlayer = async (query, priority = PRIORITY.FG_LOW, options = {}) => const findPlayer = async (query, priority = PRIORITY.FG_LOW, options = {}) =>
@ -58,18 +58,18 @@ export default (options = {}) => {
query: encodeURIComponent(query), query: encodeURIComponent(query),
}), }),
options, options,
priority, priority
); );
const rankingGlobal = async ( const rankingGlobal = async (
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchJson( fetchJson(
substituteVars(SS_API_RANKING_GLOBAL_URL, { page }), substituteVars(SS_API_RANKING_GLOBAL_URL, { page }),
options, options,
priority, priority
); );
const rankingGlobalPages = async (priority = PRIORITY.FG_LOW, options = {}) => const rankingGlobalPages = async (priority = PRIORITY.FG_LOW, options = {}) =>

@ -24,7 +24,7 @@ export const parseSsInt = (text) => {
export const parseSsFloat = (text) => export const parseSsFloat = (text) =>
text text
? parseFloat( ? parseFloat(
getFirstRegexpMatch(/([0-9,.]+)\s*$/, text.replace(/[^\d.]/g, "")), getFirstRegexpMatch(/([0-9,.]+)\s*$/, text.replace(/[^\d.]/g, ""))
) )
: null; : null;
@ -78,32 +78,32 @@ export default (options = {}) => {
const rankeds = async ( const rankeds = async (
page = 1, page = 1,
priority = PRIORITY.BG_NORMAL, priority = PRIORITY.BG_NORMAL,
options = {}, options = {}
) => ) =>
fetchJson(substituteVars(RANKEDS_URL, { page }), options, priority).then( fetchJson(substituteVars(RANKEDS_URL, { page }), options, priority).then(
(r) => { (r) => {
r.body = processRankeds(r.body); r.body = processRankeds(r.body);
return r; return r;
}, }
); );
const processPlayerProfile = (playerId, doc) => { const processPlayerProfile = (playerId, doc) => {
cfDecryptEmail(doc); cfDecryptEmail(doc);
let avatar = getImgUrl( let avatar = getImgUrl(
opt(doc.querySelector(".column.avatar img"), "src", null), opt(doc.querySelector(".column.avatar img"), "src", null)
); );
let playerName = opt( let playerName = opt(
doc.querySelector(".content .column:not(.avatar) .title a"), doc.querySelector(".content .column:not(.avatar) .title a"),
"innerText", "innerText"
); );
playerName = playerName ? playerName.trim() : null; playerName = playerName ? playerName.trim() : null;
let country = getFirstRegexpMatch( let country = getFirstRegexpMatch(
/^.*?\/flags\/([^.]+)\..*$/, /^.*?\/flags\/([^.]+)\..*$/,
opt(doc.querySelector(".content .column .title img"), "src"), opt(doc.querySelector(".content .column .title img"), "src")
); );
country = country ? country.toUpperCase() : null; country = country ? country.toUpperCase() : null;
@ -111,8 +111,8 @@ export default (options = {}) => {
opt( opt(
doc.querySelector(".pagination .pagination-list li a.is-current"), doc.querySelector(".pagination .pagination-list li a.is-current"),
"innerText", "innerText",
null, null
), )
); );
pageNum = !isNaN(pageNum) ? pageNum : null; pageNum = !isNaN(pageNum) ? pageNum : null;
@ -120,8 +120,8 @@ export default (options = {}) => {
opt( opt(
doc.querySelector(".pagination .pagination-list li:last-of-type"), doc.querySelector(".pagination .pagination-list li:last-of-type"),
"innerText", "innerText",
null, null
), )
); );
pageQty = !isNaN(pageQty) ? pageQty : null; pageQty = !isNaN(pageQty) ? pageQty : null;
@ -130,31 +130,31 @@ export default (options = {}) => {
/^\s*<strong>(?:[^:]+)\s*:?\s*<\/strong>\s*(.*)$/, /^\s*<strong>(?:[^:]+)\s*:?\s*<\/strong>\s*(.*)$/,
opt( opt(
doc.querySelector( doc.querySelector(
".columns .column:not(.is-narrow) ul li:nth-of-type(3)", ".columns .column:not(.is-narrow) ul li:nth-of-type(3)"
), ),
"innerHTML", "innerHTML"
), )
), )
); );
totalItems = !isNaN(totalItems) ? totalItems : 0; totalItems = !isNaN(totalItems) ? totalItems : 0;
let playerRank = parseSsInt( let playerRank = parseSsInt(
opt( opt(
doc.querySelector( doc.querySelector(
".content .column ul li:first-of-type a:first-of-type", ".content .column ul li:first-of-type a:first-of-type"
), ),
"innerText", "innerText"
), )
); );
playerRank = !isNaN(playerRank) ? playerRank : null; playerRank = !isNaN(playerRank) ? playerRank : null;
let countryRank = parseSsInt( let countryRank = parseSsInt(
opt( opt(
doc.querySelector( doc.querySelector(
'.content .column ul li:first-of-type a[href^="/global?country="]', '.content .column ul li:first-of-type a[href^="/global?country="]'
), ),
"innerText", "innerText"
), )
); );
countryRank = !isNaN(countryRank) ? countryRank : null; countryRank = !isNaN(countryRank) ? countryRank : null;
@ -170,7 +170,7 @@ export default (options = {}) => {
[...doc.querySelectorAll(".content .column ul li")] [...doc.querySelectorAll(".content .column ul li")]
.map((li) => { .map((li) => {
const matches = li.innerHTML.match( const matches = li.innerHTML.match(
/^\s*<strong>([^:]+)\s*:?\s*<\/strong>\s*(.*)$/, /^\s*<strong>([^:]+)\s*:?\s*<\/strong>\s*(.*)$/
); );
if (!matches) return null; if (!matches) return null;
@ -219,7 +219,7 @@ export default (options = {}) => {
const item = mapping.find((m) => m.key === matches[1]); const item = mapping.find((m) => m.key === matches[1]);
return item ? { ...item, value } : { label: matches[1], value }; return item ? { ...item, value } : { label: matches[1], value };
}) })
.filter((s) => s), .filter((s) => s)
) )
.reduce( .reduce(
(cum, item) => { (cum, item) => {
@ -255,7 +255,7 @@ export default (options = {}) => {
return cum; return cum;
}, },
{ inactiveAccount: false, bannedAccount: false }, { inactiveAccount: false, bannedAccount: false }
); );
const scores = [...doc.querySelectorAll("table.ranking tbody tr")].map( const scores = [...doc.querySelectorAll("table.ranking tbody tr")].map(
@ -274,7 +274,7 @@ export default (options = {}) => {
if (song) { if (song) {
const leaderboardId = parseInt( const leaderboardId = parseInt(
getFirstRegexpMatch(/leaderboard\/(\d+)/, song.href), getFirstRegexpMatch(/leaderboard\/(\d+)/, song.href),
10, 10
); );
ret.leaderboardId = leaderboardId ? leaderboardId : null; ret.leaderboardId = leaderboardId ? leaderboardId : null;
} else { } else {
@ -293,7 +293,7 @@ export default (options = {}) => {
.replace(/&amp;/g, "&") .replace(/&amp;/g, "&")
.replace( .replace(
/<span class="__cf_email__" data-cfemail="[^"]+">\[email&nbsp;protected]<\/span>/g, /<span class="__cf_email__" data-cfemail="[^"]+">\[email&nbsp;protected]<\/span>/g,
"", ""
) )
.match(/^(.*?)\s*<span[^>]+>(.*?)<\/span>/) .match(/^(.*?)\s*<span[^>]+>(.*?)<\/span>/)
: null; : null;
@ -328,7 +328,7 @@ export default (options = {}) => {
ret.timeSet = songDate ? dateFromString(songDate.title) : null; ret.timeSet = songDate ? dateFromString(songDate.title) : null;
const pp = parseSsFloat( const pp = parseSsFloat(
opt(tr.querySelector("th.score .scoreTop.ppValue"), "innerText"), opt(tr.querySelector("th.score .scoreTop.ppValue"), "innerText")
); );
ret.pp = !isNaN(pp) ? pp : null; ret.pp = !isNaN(pp) ? pp : null;
@ -337,9 +337,9 @@ export default (options = {}) => {
/^\(([0-9.]+)pp\)$/, /^\(([0-9.]+)pp\)$/,
opt( opt(
tr.querySelector("th.score .scoreTop.ppWeightedValue"), tr.querySelector("th.score .scoreTop.ppWeightedValue"),
"innerText", "innerText"
), )
), )
); );
ret.ppWeighted = !isNaN(ppWeighted) ? ppWeighted : null; ret.ppWeighted = !isNaN(ppWeighted) ? ppWeighted : null;
@ -380,7 +380,7 @@ export default (options = {}) => {
} }
return ret; return ret;
}, }
); );
const recentPlay = const recentPlay =
scores && scores.length && scores[0].timeSet ? scores[0].timeSet : null; scores && scores.length && scores[0].timeSet ? scores[0].timeSet : null;
@ -394,18 +394,18 @@ export default (options = {}) => {
externalProfileUrl: opt( externalProfileUrl: opt(
doc.querySelector(".content .column:not(.avatar) .title a"), doc.querySelector(".content .column:not(.avatar) .title a"),
"href", "href",
null, null
), ),
history: getFirstRegexpMatch( history: getFirstRegexpMatch(
/data:\s*\[([0-9,]+)\]/, /data:\s*\[([0-9,]+)\]/,
doc.body.innerHTML, doc.body.innerHTML
), ),
country, country,
badges: [...doc.querySelectorAll(".column.avatar center img")].map( badges: [...doc.querySelectorAll(".column.avatar center img")].map(
(img) => ({ (img) => ({
image: getImgUrl(img.src), image: getImgUrl(img.src),
description: img.title, description: img.title,
}), })
), ),
rank: stats.rank ? stats.rank : null, rank: stats.rank ? stats.rank : null,
countryRank: stats.countryRank ? stats.countryRank : null, countryRank: stats.countryRank ? stats.countryRank : null,
@ -435,7 +435,7 @@ export default (options = {}) => {
fetchHtml( fetchHtml(
substituteVars(PLAYER_PROFILE_URL, { playerId }), substituteVars(PLAYER_PROFILE_URL, { playerId }),
options, options,
priority, priority
).then((r) => { ).then((r) => {
r.body = processPlayerProfile(playerId, r.body); r.body = processPlayerProfile(playerId, r.body);
@ -451,17 +451,17 @@ export default (options = {}) => {
const id = getFirstRegexpMatch(/\/(\d+)$/, a.href); const id = getFirstRegexpMatch(/\/(\d+)$/, a.href);
const avatar = getImgUrl( const avatar = getImgUrl(
opt(tr.querySelector("td.picture img"), "src", null), opt(tr.querySelector("td.picture img"), "src", null)
); );
let country = getFirstRegexpMatch( let country = getFirstRegexpMatch(
/^.*?\/flags\/([^.]+)\..*$/, /^.*?\/flags\/([^.]+)\..*$/,
opt(tr.querySelector("td.player img"), "src", null), opt(tr.querySelector("td.player img"), "src", null)
); );
country = country ? country.toUpperCase() : null; country = country ? country.toUpperCase() : null;
let difference = parseSsInt( let difference = parseSsInt(
opt(tr.querySelector("td.diff"), "innerText", null), opt(tr.querySelector("td.diff"), "innerText", null)
); );
difference = !isNaN(difference) ? difference : null; difference = !isNaN(difference) ? difference : null;
@ -469,15 +469,15 @@ export default (options = {}) => {
playerName = playerName || playerName === "" ? playerName.trim() : null; playerName = playerName || playerName === "" ? playerName.trim() : null;
let pp = parseSsFloat( let pp = parseSsFloat(
opt(tr.querySelector("td.pp .scoreTop.ppValue"), "innerText"), opt(tr.querySelector("td.pp .scoreTop.ppValue"), "innerText")
); );
pp = !isNaN(pp) ? pp : null; pp = !isNaN(pp) ? pp : null;
let rank = parseSsInt( let rank = parseSsInt(
getFirstRegexpMatch( getFirstRegexpMatch(
/^\s*#(\d+)\s*$/, /^\s*#(\d+)\s*$/,
opt(tr.querySelector("td.rank"), "innerText", null), opt(tr.querySelector("td.rank"), "innerText", null)
), )
); );
rank = !isNaN(rank) ? rank : null; rank = !isNaN(rank) ? rank : null;
@ -491,7 +491,7 @@ export default (options = {}) => {
pp, pp,
rank, rank,
}; };
}, }
); );
return { players: data }; return { players: data };
@ -501,12 +501,12 @@ export default (options = {}) => {
country, country,
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchHtml( fetchHtml(
substituteVars(COUNTRY_RANKING_URL, { country, page }), substituteVars(COUNTRY_RANKING_URL, { country, page }),
options, options,
priority, priority
).then((r) => { ).then((r) => {
r.body = processCountryRanking(country, r.body); r.body = processCountryRanking(country, r.body);
@ -529,11 +529,11 @@ export default (options = {}) => {
}; };
ret.player.playerInfo.avatar = getImgUrl( ret.player.playerInfo.avatar = getImgUrl(
opt(tr.querySelector(".picture img"), "src", null), opt(tr.querySelector(".picture img"), "src", null)
); );
ret.score.rank = parseSsInt( ret.score.rank = parseSsInt(
opt(tr.querySelector("td.rank"), "innerText"), opt(tr.querySelector("td.rank"), "innerText")
); );
if (isNaN(ret.score.rank)) ret.score.rank = null; if (isNaN(ret.score.rank)) ret.score.rank = null;
@ -541,7 +541,7 @@ export default (options = {}) => {
if (player) { if (player) {
let country = getFirstRegexpMatch( let country = getFirstRegexpMatch(
/^.*?\/flags\/([^.]+)\..*$/, /^.*?\/flags\/([^.]+)\..*$/,
opt(player.querySelector("img"), "src", ""), opt(player.querySelector("img"), "src", "")
); );
country = country ? country.toUpperCase() : null; country = country ? country.toUpperCase() : null;
if (country) { if (country) {
@ -551,14 +551,14 @@ export default (options = {}) => {
ret.player.name = opt( ret.player.name = opt(
player.querySelector("span.songTop.pp"), player.querySelector("span.songTop.pp"),
"innerText", "innerText"
); );
ret.player.name = ret.player.name ret.player.name = ret.player.name
? ret.player.name.trim().replace("&#039;", "'") ? ret.player.name.trim().replace("&#039;", "'")
: null; : null;
ret.player.playerId = getFirstRegexpMatch( ret.player.playerId = getFirstRegexpMatch(
/\/u\/(\d+)((\?|&|#).*)?$/, /\/u\/(\d+)((\?|&|#).*)?$/,
opt(player, "href", ""), opt(player, "href", "")
); );
ret.player.playerId = ret.player.playerId ret.player.playerId = ret.player.playerId
? ret.player.playerId.trim() ? ret.player.playerId.trim()
@ -574,7 +574,7 @@ export default (options = {}) => {
ret.score.timeSetString = opt( ret.score.timeSetString = opt(
tr.querySelector("td.timeset"), tr.querySelector("td.timeset"),
"innerText", "innerText",
null, null
); );
if (ret.score.timeSetString) if (ret.score.timeSetString)
ret.score.timeSetString = ret.score.timeSetString.trim(); ret.score.timeSetString = ret.score.timeSetString.trim();
@ -602,7 +602,7 @@ export default (options = {}) => {
const diffs = [...doc.querySelectorAll(".tabs ul li a")].map((a) => { const diffs = [...doc.querySelectorAll(".tabs ul li a")].map((a) => {
let leaderboardId = parseInt( let leaderboardId = parseInt(
getFirstRegexpMatch(/leaderboard\/(\d+)$/, a.href), getFirstRegexpMatch(/leaderboard\/(\d+)$/, a.href),
10, 10
); );
if (isNaN(leaderboardId)) leaderboardId = null; if (isNaN(leaderboardId)) leaderboardId = null;
@ -615,7 +615,7 @@ export default (options = {}) => {
const currentDiffHuman = opt( const currentDiffHuman = opt(
doc.querySelector(".tabs li.is-active a span"), doc.querySelector(".tabs li.is-active a span"),
"innerText", "innerText",
null, null
); );
let diff = null; let diff = null;
@ -628,20 +628,20 @@ export default (options = {}) => {
const songName = opt( const songName = opt(
doc.querySelector( doc.querySelector(
".column.is-one-third-desktop .box:first-of-type .title a", ".column.is-one-third-desktop .box:first-of-type .title a"
), ),
"innerText", "innerText",
null, null
); );
const imageUrl = getImgUrl( const imageUrl = getImgUrl(
opt( opt(
doc.querySelector( doc.querySelector(
".column.is-one-third-desktop .box:first-of-type .columns .column.is-one-quarter img", ".column.is-one-third-desktop .box:first-of-type .columns .column.is-one-quarter img"
), ),
"src", "src",
null, null
), )
); );
const songInfo = [ const songInfo = [
@ -656,13 +656,13 @@ export default (options = {}) => {
] ]
.map((sid) => { .map((sid) => {
let songInfoBox = doc.querySelector( let songInfoBox = doc.querySelector(
".column.is-one-third-desktop .box:first-of-type", ".column.is-one-third-desktop .box:first-of-type"
); );
return { return {
...sid, ...sid,
value: songInfoBox value: songInfoBox
? songInfoBox.innerHTML.match( ? songInfoBox.innerHTML.match(
new RegExp(sid.label + ":\\s*<b>(.*?)</b>", "i"), new RegExp(sid.label + ":\\s*<b>(.*?)</b>", "i")
) )
: null, : null,
}; };
@ -708,7 +708,7 @@ export default (options = {}) => {
return cum; return cum;
}, },
{ imageUrl, stats: {} }, { imageUrl, stats: {} }
); );
const { stats, ...song } = songInfo; const { stats, ...song } = songInfo;
@ -718,9 +718,9 @@ export default (options = {}) => {
opt( opt(
doc.querySelector(".pagination .pagination-list li:last-of-type"), doc.querySelector(".pagination .pagination-list li:last-of-type"),
"innerText", "innerText",
null, null
), ),
10, 10
); );
if (isNaN(pageQty)) pageQty = null; if (isNaN(pageQty)) pageQty = null;
@ -736,7 +736,7 @@ export default (options = {}) => {
let diffChartText = getFirstRegexpMatch( let diffChartText = getFirstRegexpMatch(
/'difficulty',\s*([0-9.,\s]+)\s*\]/, /'difficulty',\s*([0-9.,\s]+)\s*\]/,
doc.body.innerHTML, doc.body.innerHTML
); );
let diffChart = (diffChartText ? diffChartText : "") let diffChart = (diffChartText ? diffChartText : "")
.split(",") .split(",")
@ -758,12 +758,12 @@ export default (options = {}) => {
leaderboardId, leaderboardId,
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchHtml( fetchHtml(
substituteVars(LEADERBOARD_URL, { leaderboardId, page }), substituteVars(LEADERBOARD_URL, { leaderboardId, page }),
options, options,
priority, priority
).then((r) => { ).then((r) => {
r.body = processLeaderboard(leaderboardId, page, r.body); r.body = processLeaderboard(leaderboardId, page, r.body);

@ -7,7 +7,7 @@ const CLIENT_ID = "u0swxz56n4iumc634at1osoqdk31qt";
const TWITCH_AUTH_URL = "https://id.twitch.tv/oauth2"; const TWITCH_AUTH_URL = "https://id.twitch.tv/oauth2";
const AUTHORIZATION_URL = const AUTHORIZATION_URL =
`${TWITCH_AUTH_URL}/authorize?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent( `${TWITCH_AUTH_URL}/authorize?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(
ssrConfig.domain + "/twitch", ssrConfig.domain + "/twitch"
)}&response_type=token` + "&scope=${scopes}&state=${state}"; )}&response_type=token` + "&scope=${scopes}&state=${state}";
const VALIDATE_URL = `${TWITCH_AUTH_URL}/validate`; const VALIDATE_URL = `${TWITCH_AUTH_URL}/validate`;
@ -26,7 +26,7 @@ export default (options = {}) => {
url, url,
accessToken, accessToken,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchJson( fetchJson(
url, url,
@ -37,7 +37,7 @@ export default (options = {}) => {
Authorization: `Bearer ${accessToken}`, Authorization: `Bearer ${accessToken}`,
}, },
}, },
priority, priority
); );
const getAuthUrl = (state = "", scopes = "") => const getAuthUrl = (state = "", scopes = "") =>
@ -49,25 +49,25 @@ export default (options = {}) => {
const validateToken = async ( const validateToken = async (
accessToken, accessToken,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchJson( fetchJson(
VALIDATE_URL, VALIDATE_URL,
{ ...options, headers: { Authorization: `OAuth ${accessToken}` } }, { ...options, headers: { Authorization: `OAuth ${accessToken}` } },
priority, priority
); );
const profile = async ( const profile = async (
accessToken, accessToken,
login, login,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchApi( fetchApi(
substituteVars(PROFILE_URL, { login: encodeURIComponent(login) }), substituteVars(PROFILE_URL, { login: encodeURIComponent(login) }),
accessToken, accessToken,
priority, priority,
options, options
); );
const videos = async ( const videos = async (
@ -75,7 +75,7 @@ export default (options = {}) => {
userId, userId,
type = "archive", type = "archive",
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchApi( fetchApi(
substituteVars(VIDEOS_URL, { substituteVars(VIDEOS_URL, {
@ -84,20 +84,20 @@ export default (options = {}) => {
}), }),
accessToken, accessToken,
priority, priority,
options, options
); );
const streams = async ( const streams = async (
accessToken, accessToken,
userId, userId,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
options = {}, options = {}
) => ) =>
fetchApi( fetchApi(
substituteVars(STREAMS_URL, { userId: encodeURIComponent(userId) }), substituteVars(STREAMS_URL, { userId: encodeURIComponent(userId) }),
accessToken, accessToken,
priority, priority,
options, options
); );
return { return {

@ -43,7 +43,7 @@ export default () => {
const getCategories = async () => { const getCategories = async () => {
const categories = await resolvePromiseOrWaitForPending( const categories = await resolvePromiseOrWaitForPending(
`accSaberCategories`, `accSaberCategories`,
() => accSaberCategoriesRepository().getAll(), () => accSaberCategoriesRepository().getAll()
); );
const getIdx = (category) => { const getIdx = (category) => {
@ -58,25 +58,25 @@ export default () => {
resolvePromiseOrWaitForPending(`accSaberPlayer/${playerId}`, () => resolvePromiseOrWaitForPending(`accSaberPlayer/${playerId}`, () =>
accSaberPlayersRepository().getAllFromIndex( accSaberPlayersRepository().getAllFromIndex(
"accsaber-players-playerId", "accsaber-players-playerId",
playerId, playerId
), )
); );
const getRanking = async (category = "overall") => const getRanking = async (category = "overall") =>
accSaberPlayersRepository().getAllFromIndex( accSaberPlayersRepository().getAllFromIndex(
"accsaber-players-category", "accsaber-players-category",
category, category
); );
const getPlayerHistory = async (playerId) => const getPlayerHistory = async (playerId) =>
resolvePromiseOrWaitForPending(`accSaberPlayerHistory/${playerId}`, () => resolvePromiseOrWaitForPending(`accSaberPlayerHistory/${playerId}`, () =>
accSaberPlayersHistoryRepository().getAllFromIndex( accSaberPlayersHistoryRepository().getAllFromIndex(
"accsaber-players-history-playerId", "accsaber-players-history-playerId",
playerId, playerId
), )
); );
const isDataForPlayerAvailable = async (playerId) => const isDataForPlayerAvailable = async (playerId) =>
(await Promise.all([getPlayer(playerId), getCategories()])).every( (await Promise.all([getPlayer(playerId), getCategories()])).every(
(d) => d?.length, (d) => d?.length
); );
const getPlayerGain = (playerHistory, daysAgo = 1, maxDaysAgo = 7) => const getPlayerGain = (playerHistory, daysAgo = 1, maxDaysAgo = 7) =>
@ -85,7 +85,7 @@ export default () => {
toAccSaberMidnight, toAccSaberMidnight,
"accSaberDate", "accSaberDate",
daysAgo, daysAgo,
maxDaysAgo, maxDaysAgo
); );
const getLastUpdatedKey = (type) => `accSaber${capitalize(type)}LastUpdated`; const getLastUpdatedKey = (type) => `accSaber${capitalize(type)}LastUpdated`;
@ -100,9 +100,9 @@ export default () => {
if (lastUpdated && lastUpdated > new Date() - REFRESH_INTERVAL) { if (lastUpdated && lastUpdated > new Date() - REFRESH_INTERVAL) {
log.debug( log.debug(
`Refresh interval for ${type} not yet expired, skipping. Next refresh on ${formatDate( `Refresh interval for ${type} not yet expired, skipping. Next refresh on ${formatDate(
addToDate(REFRESH_INTERVAL, lastUpdated), addToDate(REFRESH_INTERVAL, lastUpdated)
)}`, )}`,
"AccSaberService", "AccSaberService"
); );
return false; return false;
@ -116,7 +116,7 @@ export default () => {
playerId, playerId,
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
{ ...options } = {}, { ...options } = {}
) => { ) => {
if (!options) options = {}; if (!options) options = {};
if (!options.hasOwnProperty("cacheTtl")) if (!options.hasOwnProperty("cacheTtl"))
@ -124,7 +124,7 @@ export default () => {
const categoriesByDisplayName = convertArrayToObjectByKey( const categoriesByDisplayName = convertArrayToObjectByKey(
await getCategories(), await getCategories(),
"displayName", "displayName"
); );
return ( return (
@ -136,7 +136,7 @@ export default () => {
playerId, playerId,
page, page,
priority, priority,
}), })
) )
).map((s) => ({ ).map((s) => ({
...s, ...s,
@ -150,7 +150,7 @@ export default () => {
}; };
const getScoresHistogramDefinition = ( const getScoresHistogramDefinition = (
serviceParams = { type: "overall", sort: "ap", order: "desc" }, serviceParams = { type: "overall", sort: "ap", order: "desc" }
) => { ) => {
const scoreType = serviceParams?.type ?? "overall"; const scoreType = serviceParams?.type ?? "overall";
const sort = serviceParams?.sort ?? "ap"; const sort = serviceParams?.sort ?? "ap";
@ -258,7 +258,7 @@ export default () => {
const getPlayerScoresPage = async ( const getPlayerScoresPage = async (
playerId, playerId,
serviceParams = { sort: "recent", order: "desc", page: 1 }, serviceParams = { sort: "recent", order: "desc", page: 1 }
) => { ) => {
let page = serviceParams?.page ?? 1; let page = serviceParams?.page ?? 1;
if (page < 1) page = 1; if (page < 1) page = 1;
@ -290,7 +290,7 @@ export default () => {
const fetchPlayerRankHistory = async ( const fetchPlayerRankHistory = async (
playerId, playerId,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
{ ...options } = {}, { ...options } = {}
) => { ) => {
if (!options) options = {}; if (!options) options = {};
if (!options.hasOwnProperty("cacheTtl")) if (!options.hasOwnProperty("cacheTtl"))
@ -306,13 +306,13 @@ export default () => {
const refreshCategories = async ( const refreshCategories = async (
forceUpdate = false, forceUpdate = false,
priority = queues.PRIORITY.BG_NORMAL, priority = queues.PRIORITY.BG_NORMAL,
throwErrors = false, throwErrors = false
) => { ) => {
log.debug( log.debug(
`Starting AccSaber categories refreshing${ `Starting AccSaber categories refreshing${
forceUpdate ? " (forced)" : "" forceUpdate ? " (forced)" : ""
}...`, }...`,
"AccSaberService", "AccSaberService"
); );
try { try {
@ -327,7 +327,7 @@ export default () => {
log.trace( log.trace(
`Fetching current categories from AccSaber...`, `Fetching current categories from AccSaber...`,
"AccSaberService", "AccSaberService"
); );
let categories = await accSaberCategoriesApiClient.getProcessed({ let categories = await accSaberCategoriesApiClient.getProcessed({
@ -352,13 +352,13 @@ export default () => {
const dbCategoriesNames = dbCategories.map((c) => c.name); const dbCategoriesNames = dbCategories.map((c) => c.name);
const newCategories = categories.filter( const newCategories = categories.filter(
(c) => !dbCategories || !dbCategoriesNames.includes(c.name), (c) => !dbCategories || !dbCategoriesNames.includes(c.name)
); );
if (newCategories && newCategories.length) if (newCategories && newCategories.length)
log.debug( log.debug(
`${newCategories.length} new categories found`, `${newCategories.length} new categories found`,
"AccSaberService", "AccSaberService"
); );
await db.runInTransaction( await db.runInTransaction(
@ -385,14 +385,14 @@ export default () => {
log.trace(`Updating categories in DB...`, "AccSaberService"); log.trace(`Updating categories in DB...`, "AccSaberService");
await Promise.all( await Promise.all(
categories.map(async (c) => accSaberCategoriesStore.put(c)), categories.map(async (c) => accSaberCategoriesStore.put(c))
); );
log.trace(`Categories updated`, "AccSaberService"); log.trace(`Categories updated`, "AccSaberService");
log.trace( log.trace(
`Updating categories last update date in DB...`, `Updating categories last update date in DB...`,
"AccSaberService", "AccSaberService"
); );
await tx await tx
@ -400,13 +400,13 @@ export default () => {
.put(new Date(), getLastUpdatedKey("categories")); .put(new Date(), getLastUpdatedKey("categories"));
log.debug(`Categories last update date updated`, "AccSaberService"); log.debug(`Categories last update date updated`, "AccSaberService");
}, }
); );
accSaberCategoriesRepository().addToCache(categories); accSaberCategoriesRepository().addToCache(categories);
keyValueRepository().setCache( keyValueRepository().setCache(
getLastUpdatedKey("categories"), getLastUpdatedKey("categories"),
new Date(), new Date()
); );
log.debug(`Categories refreshing completed`, "AccSaberService"); log.debug(`Categories refreshing completed`, "AccSaberService");
@ -427,7 +427,7 @@ export default () => {
try { try {
log.debug( log.debug(
`Updating player ${player.playerId} history`, `Updating player ${player.playerId} history`,
"AccSaberService", "AccSaberService"
); );
const accSaberDate = toAccSaberMidnight(new Date()); const accSaberDate = toAccSaberMidnight(new Date());
@ -441,9 +441,9 @@ export default () => {
`Refresh interval for player ${ `Refresh interval for player ${
player.playerId player.playerId
} history not yet expired, skipping. Next refresh on ${formatDate( } history not yet expired, skipping. Next refresh on ${formatDate(
addToDate(REFRESH_INTERVAL, lastUpdated), addToDate(REFRESH_INTERVAL, lastUpdated)
)}`, )}`,
"AccSaberService", "AccSaberService"
); );
return; return;
@ -452,7 +452,7 @@ export default () => {
const categories = (await getCategories())?.map((c) => c.name) ?? null; const categories = (await getCategories())?.map((c) => c.name) ?? null;
if (!categories) { if (!categories) {
log.trace( log.trace(
`No categories found, skip updating player ${player.playerId} history.`, `No categories found, skip updating player ${player.playerId} history.`
); );
return; return;
} }
@ -460,7 +460,7 @@ export default () => {
let accStats = {}; let accStats = {};
for (const category of categories) { for (const category of categories) {
const playerAccInfo = ((await getRanking(category)) ?? []).find( const playerAccInfo = ((await getRanking(category)) ?? []).find(
(p) => p.playerId === player.playerId, (p) => p.playerId === player.playerId
); );
if (!playerAccInfo) continue; if (!playerAccInfo) continue;
@ -492,7 +492,7 @@ export default () => {
} else { } else {
log.trace( log.trace(
`No Acc Saber data for player ${player.playerId}, skipping history updating.`, `No Acc Saber data for player ${player.playerId}, skipping history updating.`,
"AccSaberService", "AccSaberService"
); );
return; return;
@ -503,7 +503,7 @@ export default () => {
log.debug( log.debug(
`Player ${player.playerId} history updating error.`, `Player ${player.playerId} history updating error.`,
"AccSaberService", "AccSaberService",
e, e
); );
} }
}; };
@ -512,13 +512,13 @@ export default () => {
category = "overall", category = "overall",
forceUpdate = false, forceUpdate = false,
priority = queues.PRIORITY.BG_NORMAL, priority = queues.PRIORITY.BG_NORMAL,
throwErrors = false, throwErrors = false
) => { ) => {
log.debug( log.debug(
`Starting AccSaber ${category} ranking refreshing${ `Starting AccSaber ${category} ranking refreshing${
forceUpdate ? " (forced)" : "" forceUpdate ? " (forced)" : ""
}...`, }...`,
"AccSaberService", "AccSaberService"
); );
try { try {
@ -535,7 +535,7 @@ export default () => {
log.trace( log.trace(
`Fetching current ${category} ranking from AccSaber...`, `Fetching current ${category} ranking from AccSaber...`,
"AccSaberService", "AccSaberService"
); );
const ranking = await accSaberRankingApiClient.getProcessed({ const ranking = await accSaberRankingApiClient.getProcessed({
@ -545,7 +545,7 @@ export default () => {
if (!ranking || !ranking.length) { if (!ranking || !ranking.length) {
log.warn( log.warn(
`AccSaber returned empty ${category} ranking`, `AccSaber returned empty ${category} ranking`,
"AccSaberService", "AccSaberService"
); );
return null; return null;
@ -554,7 +554,7 @@ export default () => {
log.trace( log.trace(
`${capitalize(category)} ranking fetched`, `${capitalize(category)} ranking fetched`,
"AccSaberService", "AccSaberService",
ranking, ranking
); );
log.trace(`Updating ${category} ranking...`, "AccSaberService"); log.trace(`Updating ${category} ranking...`, "AccSaberService");
@ -570,7 +570,7 @@ export default () => {
log.trace( log.trace(
`Remove old players from DB for category ${category}`, `Remove old players from DB for category ${category}`,
"AccSaberService", "AccSaberService"
); );
while (cursor) { while (cursor) {
@ -589,14 +589,14 @@ export default () => {
log.trace(`Updating players in DB...`, "AccSaberService"); log.trace(`Updating players in DB...`, "AccSaberService");
await Promise.all( await Promise.all(
ranking.map(async (p) => accSaberPlayersStore.put(p)), ranking.map(async (p) => accSaberPlayersStore.put(p))
); );
log.trace(`Players updated`, "AccSaberService"); log.trace(`Players updated`, "AccSaberService");
log.trace( log.trace(
`Updating players last update date in DB...`, `Updating players last update date in DB...`,
"AccSaberService", "AccSaberService"
); );
await tx await tx
@ -604,7 +604,7 @@ export default () => {
.put(new Date(), getLastUpdatedKey(rankingType)); .put(new Date(), getLastUpdatedKey(rankingType));
log.debug(`Players last update date updated`, "AccSaberService"); log.debug(`Players last update date updated`, "AccSaberService");
}, }
); );
accSaberPlayersRepository().addToCache(ranking); accSaberPlayersRepository().addToCache(ranking);
@ -612,7 +612,7 @@ export default () => {
log.debug( log.debug(
`${capitalize(category)} ranking refreshing completed`, `${capitalize(category)} ranking refreshing completed`,
"AccSaberService", "AccSaberService"
); );
return ranking.sort((a, b) => a.rank - b.rank); return ranking.sort((a, b) => a.rank - b.rank);
@ -622,7 +622,7 @@ export default () => {
log.debug( log.debug(
` ${capitalize(category)} ranking refreshing error`, ` ${capitalize(category)} ranking refreshing error`,
"AccSaberService", "AccSaberService",
e, e
); );
return null; return null;
@ -633,13 +633,13 @@ export default () => {
category = "overall", category = "overall",
forceUpdate = false, forceUpdate = false,
priority = queues.PRIORITY.BG_NORMAL, priority = queues.PRIORITY.BG_NORMAL,
throwErrors = false, throwErrors = false
) => { ) => {
log.trace( log.trace(
`Starting AccSaber all data refreshing${ `Starting AccSaber all data refreshing${
forceUpdate ? " (forced)" : "" forceUpdate ? " (forced)" : ""
}...`, }...`,
"AccSaberService", "AccSaberService"
); );
try { try {
@ -650,7 +650,7 @@ export default () => {
const allRankings = await Promise.all( const allRankings = await Promise.all(
dbCategories.all dbCategories.all
.map((c) => c.name) .map((c) => c.name)
.map(async (category) => refreshRanking(category)), .map(async (category) => refreshRanking(category))
); );
log.debug(`All data refreshing completed.`, "AccSaberService"); log.debug(`All data refreshing completed.`, "AccSaberService");
@ -665,8 +665,8 @@ export default () => {
Promise.all( Promise.all(
(await playerService.getAllActive()).map(async (player) => (await playerService.getAllActive()).map(async (player) =>
updatePlayerHistory(player), updatePlayerHistory(player)
), )
).then((_) => _); ).then((_) => _);
return dbCategories.all.map((c) => ({ return dbCategories.all.map((c) => ({

@ -52,7 +52,7 @@ export default () => {
suspension.activeTo = addToDate( suspension.activeTo = addToDate(
Math.pow(2, suspension.count) * HOUR, Math.pow(2, suspension.count) * HOUR,
suspension.activeTo, suspension.activeTo
); );
suspension.count++; suspension.count++;
@ -97,7 +97,7 @@ export default () => {
si.diffs.forEach((d) => { si.diffs.forEach((d) => {
const newNotesCnt = INVALID_NOTES_COUNT_FIXES[hash].find( const newNotesCnt = INVALID_NOTES_COUNT_FIXES[hash].find(
(f) => f.type === d?.characteristic && f.diff === d?.difficulty, (f) => f.type === d?.characteristic && f.diff === d?.difficulty
); );
if (!newNotesCnt) return; if (!newNotesCnt) return;
@ -114,7 +114,7 @@ export default () => {
forceUpdate = false, forceUpdate = false,
cacheOnly = false, cacheOnly = false,
errSongId = "", errSongId = "",
hash = null, hash = null
) => { ) => {
if (!forceUpdate && songInfo) return fixInvalidNotesCount(hash, songInfo); if (!forceUpdate && songInfo) return fixInvalidNotesCount(hash, songInfo);
@ -158,7 +158,7 @@ export default () => {
forceUpdate = false, forceUpdate = false,
cacheOnly = false, cacheOnly = false,
signal = null, signal = null,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW
) => { ) => {
hash = hash.toLowerCase(); hash = hash.toLowerCase();
@ -170,7 +170,7 @@ export default () => {
forceUpdate, forceUpdate,
cacheOnly, cacheOnly,
hash, hash,
hash, hash
); );
}; };
@ -179,13 +179,13 @@ export default () => {
forceUpdate = false, forceUpdate = false,
cacheOnly = false, cacheOnly = false,
signal = null, signal = null,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW
) => { ) => {
key = key.toLowerCase(); key = key.toLowerCase();
const songInfo = await songsBeatMapsRepository().getFromIndex( const songInfo = await songsBeatMapsRepository().getFromIndex(
"songs-beatmaps-key", "songs-beatmaps-key",
key, key
); );
return fetchSong( return fetchSong(
@ -193,7 +193,7 @@ export default () => {
() => keyApiClient.getProcessed({ key, signal, priority }), () => keyApiClient.getProcessed({ key, signal, priority }),
forceUpdate, forceUpdate,
cacheOnly, cacheOnly,
key, key
); );
}; };
@ -254,7 +254,7 @@ export default () => {
}, },
stars: null, stars: null,
}; };
}), })
) )
.filter((diff) => diff); .filter((diff) => diff);
}, []); }, []);

@ -40,7 +40,7 @@ export default () => {
const getPlayerScores = async (playerId) => const getPlayerScores = async (playerId) =>
resolvePromiseOrWaitForPending(`getPlayerScores/${playerId}`, () => resolvePromiseOrWaitForPending(`getPlayerScores/${playerId}`, () =>
beatSaviorRepository().getAllFromIndex("beat-savior-playerId", playerId), beatSaviorRepository().getAllFromIndex("beat-savior-playerId", playerId)
); );
const getPlayerScoresWithScoreSaber = async (playerId) => { const getPlayerScoresWithScoreSaber = async (playerId) => {
@ -50,8 +50,8 @@ export default () => {
scoresService.getPlayerScoresAsObject( scoresService.getPlayerScoresAsObject(
playerId, playerId,
(score) => score?.leaderboard?.song?.hash?.toLowerCase() ?? null, (score) => score?.leaderboard?.song?.hash?.toLowerCase() ?? null,
true, true
), )
), ),
]); ]);
@ -61,7 +61,7 @@ export default () => {
const ssScore = const ssScore =
playerScores[bsData.hash.toLowerCase()].find((ssScore) => playerScores[bsData.hash.toLowerCase()].find((ssScore) =>
isScoreMatchingBsData(ssScore, bsData, true), isScoreMatchingBsData(ssScore, bsData, true)
) ?? null; ) ?? null;
return { return {
@ -101,7 +101,7 @@ export default () => {
}; };
const getScoresHistogramDefinition = ( const getScoresHistogramDefinition = (
serviceParams = { sort: "recent", order: "desc" }, serviceParams = { sort: "recent", order: "desc" }
) => { ) => {
const sort = serviceParams?.sort ?? "recent"; const sort = serviceParams?.sort ?? "recent";
const order = serviceParams?.order ?? "desc"; const order = serviceParams?.order ?? "desc";
@ -188,7 +188,7 @@ export default () => {
const getPlayerScoresPage = async ( const getPlayerScoresPage = async (
playerId, playerId,
serviceParams = { sort: "recent", order: "desc", page: 1 }, serviceParams = { sort: "recent", order: "desc", page: 1 }
) => { ) => {
let page = serviceParams?.page ?? 1; let page = serviceParams?.page ?? 1;
if (page < 1) page = 1; if (page < 1) page = 1;
@ -250,14 +250,14 @@ export default () => {
const updateData = async (playerId, data) => { const updateData = async (playerId, data) => {
log.debug( log.debug(
`Updating Beat Savior data for player "${playerId}"...`, `Updating Beat Savior data for player "${playerId}"...`,
"BeatSaviorService", "BeatSaviorService"
); );
await Promise.all(data.map(async (d) => beatSaviorRepository().set(d))); await Promise.all(data.map(async (d) => beatSaviorRepository().set(d)));
log.debug( log.debug(
`Update player "${playerId}" Beat Savior last refresh date...`, `Update player "${playerId}" Beat Savior last refresh date...`,
"BeatSaviorService", "BeatSaviorService"
); );
await beatSaviorPlayersRepository().set({ await beatSaviorPlayersRepository().set({
@ -267,7 +267,7 @@ export default () => {
log.debug( log.debug(
`Beat Savior data for player "${playerId}" updated.`, `Beat Savior data for player "${playerId}" updated.`,
"BeatSaviorService", "BeatSaviorService"
); );
return data; return data;
@ -277,7 +277,7 @@ export default () => {
try { try {
log.debug( log.debug(
`Fetching Beat Savior data for player "${playerId}"...`, `Fetching Beat Savior data for player "${playerId}"...`,
"BeatSaviorService", "BeatSaviorService"
); );
const data = await beatSaviorApiClient.getProcessed({ const data = await beatSaviorApiClient.getProcessed({
@ -287,7 +287,7 @@ export default () => {
if (!data) { if (!data) {
log.debug( log.debug(
`No Beat Savior data for player "${playerId}"`, `No Beat Savior data for player "${playerId}"`,
"BeatSaviorService", "BeatSaviorService"
); );
return null; return null;
@ -298,7 +298,7 @@ export default () => {
log.trace( log.trace(
`Beat Savior data for player "${playerId}" fetched`, `Beat Savior data for player "${playerId}" fetched`,
"BeatSaviorService", "BeatSaviorService",
data, data
); );
return updateData(playerId, data); return updateData(playerId, data);
@ -313,13 +313,13 @@ export default () => {
playerId, playerId,
force = false, force = false,
priority = PRIORITY.BG_NORMAL, priority = PRIORITY.BG_NORMAL,
throwErrors = false, throwErrors = false
) => { ) => {
log.trace( log.trace(
`Starting refreshing BeatSavior for player "${playerId}" ${ `Starting refreshing BeatSavior for player "${playerId}" ${
force ? " (forced)" : "" force ? " (forced)" : ""
}...`, }...`,
"BeatSaviorService", "BeatSaviorService"
); );
try { try {
@ -339,9 +339,9 @@ export default () => {
if (!force && bsPlayerInfo && nextUpdate > new Date()) { if (!force && bsPlayerInfo && nextUpdate > new Date()) {
log.debug( log.debug(
`Beat Savior data is still fresh, skipping. Next refresh on ${formatDate( `Beat Savior data is still fresh, skipping. Next refresh on ${formatDate(
nextUpdate, nextUpdate
)}`, )}`,
"BeatSaviorService", "BeatSaviorService"
); );
return null; return null;
@ -349,7 +349,7 @@ export default () => {
if (player) { if (player) {
log.trace( log.trace(
`Player "${playerId}" is a cached one, checking recent play date`, `Player "${playerId}" is a cached one, checking recent play date`,
"BeatSaviorService", "BeatSaviorService"
); );
if ( if (
@ -358,7 +358,7 @@ export default () => {
) { ) {
log.debug( log.debug(
`Beat Savior data for player "${playerId}" was refreshed after recent play, skipping`, `Beat Savior data for player "${playerId}" was refreshed after recent play, skipping`,
"BeatSaviorService", "BeatSaviorService"
); );
return null; return null;
@ -367,7 +367,7 @@ export default () => {
} }
return resolvePromiseOrWaitForPending(`refresh/${playerId}`, () => return resolvePromiseOrWaitForPending(`refresh/${playerId}`, () =>
fetchPlayer(playerId, priority), fetchPlayer(playerId, priority)
); );
} catch (e) { } catch (e) {
if (throwErrors) throw e; if (throwErrors) throw e;
@ -377,7 +377,7 @@ export default () => {
e.toString ? `: ${e.toString()}` : "" e.toString ? `: ${e.toString()}` : ""
}`, }`,
"BeatSaviorService", "BeatSaviorService",
e, e
); );
return null; return null;
@ -387,13 +387,13 @@ export default () => {
const refreshAll = async ( const refreshAll = async (
force = false, force = false,
priority = PRIORITY.BG_NORMAL, priority = PRIORITY.BG_NORMAL,
throwErrors = false, throwErrors = false
) => { ) => {
log.trace( log.trace(
`Starting refreshing Beat Savior data for all players${ `Starting refreshing Beat Savior data for all players${
force ? " (forced)" : "" force ? " (forced)" : ""
}...`, }...`,
"BeatSaviorService", "BeatSaviorService"
); );
const allPlayers = await playerService.getAll(); const allPlayers = await playerService.getAll();
@ -409,15 +409,15 @@ export default () => {
player.playerId, player.playerId,
force, force,
priority, priority,
throwErrors, throwErrors
), ),
})), }))
); );
log.trace( log.trace(
`Beat Savior data for all players refreshed.`, `Beat Savior data for all players refreshed.`,
"BeatSaviorService", "BeatSaviorService",
allRefreshed, allRefreshed
); );
return allRefreshed; return allRefreshed;
@ -430,7 +430,7 @@ export default () => {
if (!playerBsData || !playerBsData.length) return null; if (!playerBsData || !playerBsData.length) return null;
const bsData = playerBsData.find((bsData) => const bsData = playerBsData.find((bsData) =>
isScoreMatchingBsData(score, bsData, true), isScoreMatchingBsData(score, bsData, true)
); );
return bsData ? bsData : null; return bsData ? bsData : null;
@ -439,7 +439,7 @@ export default () => {
const isDataForPlayerAvailable = async (playerId) => const isDataForPlayerAvailable = async (playerId) =>
(await beatSaviorRepository().getFromIndex( (await beatSaviorRepository().getFromIndex(
"beat-savior-playerId", "beat-savior-playerId",
playerId, playerId
)) !== undefined; )) !== undefined;
const destroyService = () => { const destroyService = () => {

@ -33,7 +33,7 @@ export default () => {
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
signal = null, signal = null,
force = false, force = false
) => ) =>
resolvePromiseOrWaitForPending( resolvePromiseOrWaitForPending(
`pageClient/leaderboard/${leaderboardId}/${page}`, `pageClient/leaderboard/${leaderboardId}/${page}`,
@ -44,7 +44,7 @@ export default () => {
signal, signal,
priority, priority,
cacheTtl: MINUTE, cacheTtl: MINUTE,
}), })
); );
const fetchAccSaberPage = async ( const fetchAccSaberPage = async (
@ -52,7 +52,7 @@ export default () => {
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
signal = null, signal = null,
force = false, force = false
) => { ) => {
if (page < 1) page = 1; if (page < 1) page = 1;
@ -65,7 +65,7 @@ export default () => {
signal, signal,
priority, priority,
cacheTtl: ACCSABER_LEADERBOARD_NETWORK_TTL, cacheTtl: ACCSABER_LEADERBOARD_NETWORK_TTL,
}), })
); );
if (!data || !data.scores) return data; if (!data || !data.scores) return data;
@ -77,7 +77,7 @@ export default () => {
...data, ...data,
scores: data.scores.slice( scores: data.scores.slice(
startIdx, startIdx,
startIdx + ACCSABER_LEADERBOARD_SCORES_PER_PAGE, startIdx + ACCSABER_LEADERBOARD_SCORES_PER_PAGE
), ),
}; };
}; };
@ -85,7 +85,7 @@ export default () => {
const getFriendsLeaderboard = async ( const getFriendsLeaderboard = async (
leaderboardId, leaderboardId,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
signal = null, signal = null
) => { ) => {
const leaderboard = await resolvePromiseOrWaitForPending( const leaderboard = await resolvePromiseOrWaitForPending(
`pageClient/leaderboard/${leaderboardId}/1`, `pageClient/leaderboard/${leaderboardId}/1`,
@ -96,7 +96,7 @@ export default () => {
signal, signal,
priority, priority,
cacheTtl: MINUTE, cacheTtl: MINUTE,
}), })
); );
const friends = convertArrayToObjectByKey(await friendsPromise, "playerId"); const friends = convertArrayToObjectByKey(await friendsPromise, "playerId");

@ -51,7 +51,7 @@ export default () => {
const getFriends = async () => const getFriends = async () =>
(await getAll()) (await getAll())
.filter( .filter(
(player) => player && player.playerId && !isPlayerMain(player.playerId), (player) => player && player.playerId && !isPlayerMain(player.playerId)
) )
.map((p) => p.playerId); .map((p) => p.playerId);
@ -64,7 +64,7 @@ export default () => {
player && player &&
player.playerInfo && player.playerInfo &&
!player.playerInfo.inactive && !player.playerInfo.inactive &&
!player.playerInfo.banned, !player.playerInfo.banned
); );
}; };
@ -107,13 +107,13 @@ export default () => {
const updatePlayer = async ( const updatePlayer = async (
player, player,
waitForSaving = true, waitForSaving = true,
forceAdd = false, forceAdd = false
) => { ) => {
if (!player || !player.playerId) { if (!player || !player.playerId) {
log.warn( log.warn(
`Can not update player, empty playerId`, `Can not update player, empty playerId`,
"PlayerService", "PlayerService",
player, player
); );
} }
@ -135,8 +135,8 @@ export default () => {
resolvePromiseOrWaitForPending(`playerHistory/${playerId}`, () => resolvePromiseOrWaitForPending(`playerHistory/${playerId}`, () =>
playersHistoryRepository().getAllFromIndex( playersHistoryRepository().getAllFromIndex(
"players-history-playerId", "players-history-playerId",
playerId, playerId
), )
); );
const getPlayerGain = (playerHistory, daysAgo = 1, maxDaysAgo = 7) => const getPlayerGain = (playerHistory, daysAgo = 1, maxDaysAgo = 7) =>
@ -145,7 +145,7 @@ export default () => {
toSsMidnight, toSsMidnight,
"ssDate", "ssDate",
daysAgo, daysAgo,
maxDaysAgo, maxDaysAgo
); );
const updatePlayerHistory = async (player) => { const updatePlayerHistory = async (player) => {
@ -196,7 +196,7 @@ export default () => {
if (badges?.length) if (badges?.length)
accStats.accBadges = badges.reduce( accStats.accBadges = badges.reduce(
(cum, b) => ({ ...cum, [b.label]: b.value }), (cum, b) => ({ ...cum, [b.label]: b.value }),
{}, {}
); );
} }
@ -241,7 +241,7 @@ export default () => {
const updatePlayerRecentPlay = async ( const updatePlayerRecentPlay = async (
playerId, playerId,
recentPlay, recentPlay,
recentPlayLastUpdated = new Date(), recentPlayLastUpdated = new Date()
) => { ) => {
let player; let player;
@ -277,19 +277,19 @@ export default () => {
try { try {
const player = await resolvePromiseOrWaitForPending( const player = await resolvePromiseOrWaitForPending(
`pageClient/${playerId}`, `pageClient/${playerId}`,
() => playerPageClient.getProcessed({ playerId }), () => playerPageClient.getProcessed({ playerId })
); );
const recentPlay = opt(player, "playerInfo.recentPlay"); const recentPlay = opt(player, "playerInfo.recentPlay");
const recentPlayLastUpdated = opt( const recentPlayLastUpdated = opt(
player, player,
"playerInfo.recentPlayLastUpdated", "playerInfo.recentPlayLastUpdated"
); );
if (!recentPlay || !recentPlayLastUpdated) return null; if (!recentPlay || !recentPlayLastUpdated) return null;
return updatePlayerRecentPlay( return updatePlayerRecentPlay(
playerId, playerId,
recentPlay, recentPlay,
recentPlayLastUpdated, recentPlayLastUpdated
); );
} catch (err) { } catch (err) {
// swallow error // swallow error
@ -304,7 +304,7 @@ export default () => {
const fetchPlayer = async ( const fetchPlayer = async (
playerId, playerId,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
{ fullResponse = false, ...options } = {}, { fullResponse = false, ...options } = {}
) => ) =>
resolvePromiseOrWaitForPending( resolvePromiseOrWaitForPending(
`apiClient/${playerId}/${fullResponse}`, `apiClient/${playerId}/${fullResponse}`,
@ -314,13 +314,13 @@ export default () => {
playerId, playerId,
priority, priority,
fullResponse, fullResponse,
}), })
); );
const findPlayer = async ( const findPlayer = async (
query, query,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
{ fullResponse = false, ...options } = {}, { fullResponse = false, ...options } = {}
) => ) =>
resolvePromiseOrWaitForPending( resolvePromiseOrWaitForPending(
`apiClient/find/${query}/${fullResponse}`, `apiClient/find/${query}/${fullResponse}`,
@ -330,7 +330,7 @@ export default () => {
query, query,
priority, priority,
fullResponse, fullResponse,
}), })
); );
const fetchPlayerOrGetFromCache = async ( const fetchPlayerOrGetFromCache = async (
@ -338,7 +338,7 @@ export default () => {
refreshInterval = MINUTE, refreshInterval = MINUTE,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
signal = null, signal = null,
force = false, force = false
) => { ) => {
const player = await getPlayer(playerId); const player = await getPlayer(playerId);
@ -358,7 +358,7 @@ export default () => {
...getDataFromResponse(fetchedPlayerResponse), ...getDataFromResponse(fetchedPlayerResponse),
profileLastUpdated: new Date(), profileLastUpdated: new Date(),
}, },
false, false
).then((player) => { ).then((player) => {
fetchPlayerAndUpdateRecentPlay(player.playerId); fetchPlayerAndUpdateRecentPlay(player.playerId);
@ -376,17 +376,17 @@ export default () => {
force = false, force = false,
priority = PRIORITY.BG_NORMAL, priority = PRIORITY.BG_NORMAL,
throwErrors = false, throwErrors = false,
addIfNotExists = false, addIfNotExists = false
) => { ) => {
log.trace( log.trace(
`Starting refreshing player "${playerId}" ${force ? " (forced)" : ""}...`, `Starting refreshing player "${playerId}" ${force ? " (forced)" : ""}...`,
"PlayerService", "PlayerService"
); );
if (!playerId) { if (!playerId) {
log.warn( log.warn(
`Can not refresh player if an empty playerId is given`, `Can not refresh player if an empty playerId is given`,
"PlayerService", "PlayerService"
); );
return null; return null;
@ -407,9 +407,9 @@ export default () => {
if (profileFreshnessDate > new Date()) { if (profileFreshnessDate > new Date()) {
log.debug( log.debug(
`Profile is still fresh, skipping. Next refresh on ${formatDate( `Profile is still fresh, skipping. Next refresh on ${formatDate(
profileFreshnessDate, profileFreshnessDate
)}`, )}`,
"PlayerService", "PlayerService"
); );
return player; return player;
@ -418,7 +418,7 @@ export default () => {
log.trace( log.trace(
`Fetching player ${playerId} from ScoreSaber...`, `Fetching player ${playerId} from ScoreSaber...`,
"PlayerService", "PlayerService"
); );
const fetchedPlayer = await fetchPlayer(playerId, priority); const fetchedPlayer = await fetchPlayer(playerId, priority);
@ -432,7 +432,7 @@ export default () => {
) { ) {
log.warn( log.warn(
`ScoreSaber returned empty info for player ${playerId}`, `ScoreSaber returned empty info for player ${playerId}`,
"PlayerService", "PlayerService"
); );
return null; return null;
@ -443,7 +443,7 @@ export default () => {
player = await updatePlayer( player = await updatePlayer(
{ ...fetchedPlayer, profileLastUpdated: new Date() }, { ...fetchedPlayer, profileLastUpdated: new Date() },
true, true,
addIfNotExists, addIfNotExists
); );
updatePlayerHistory(player).then((_) => _); updatePlayerHistory(player).then((_) => _);
@ -457,7 +457,7 @@ export default () => {
log.debug( log.debug(
`Player refreshing error${e.toString ? `: ${e.toString()}` : ""}`, `Player refreshing error${e.toString ? `: ${e.toString()}` : ""}`,
"PlayerService", "PlayerService",
e, e
); );
return null; return null;
@ -467,11 +467,11 @@ export default () => {
const refreshAll = async ( const refreshAll = async (
force = false, force = false,
priority = PRIORITY.BG_NORMAL, priority = PRIORITY.BG_NORMAL,
throwErrors = false, throwErrors = false
) => { ) => {
log.trace( log.trace(
`Starting refreshing all players${force ? " (forced)" : ""}...`, `Starting refreshing all players${force ? " (forced)" : ""}...`,
"PlayerService", "PlayerService"
); );
const allPlayers = await getAll(); const allPlayers = await getAll();
@ -482,8 +482,8 @@ export default () => {
const allRefreshed = await Promise.all( const allRefreshed = await Promise.all(
allPlayers.map((player) => allPlayers.map((player) =>
refresh(player.playerId, force, priority, throwErrors), refresh(player.playerId, force, priority, throwErrors)
), )
); );
log.trace(`All players refreshed.`, "PlayerService", allRefreshed); log.trace(`All players refreshed.`, "PlayerService", allRefreshed);

@ -18,7 +18,7 @@ export default () => {
scores scores
.filter((s) => s.pp > 0) .filter((s) => s.pp > 0)
.map((s) => s.pp) .map((s) => s.pp)
.sort((a, b) => b - a), .sort((a, b) => b - a)
) )
: null; : null;
@ -26,10 +26,10 @@ export default () => {
getTotalPp( getTotalPp(
Object.values({ Object.values({
...(await resolvePromiseOrWaitForPending(`scores/${playerId}`, () => ...(await resolvePromiseOrWaitForPending(`scores/${playerId}`, () =>
scoresService.getPlayerScoresAsObject(playerId), scoresService.getPlayerScoresAsObject(playerId)
)), )),
...modifiedScores, ...modifiedScores,
}), })
); );
async function getWhatIfScore(playerId, leaderboardId, pp = 0) { async function getWhatIfScore(playerId, leaderboardId, pp = 0) {

@ -35,11 +35,11 @@ export default () => {
const refreshRankeds = async ( const refreshRankeds = async (
forceUpdate = false, forceUpdate = false,
priority = queues.PRIORITY.BG_NORMAL, priority = queues.PRIORITY.BG_NORMAL,
throwErrors = false, throwErrors = false
) => { ) => {
log.trace( log.trace(
`Starting rankeds refreshing${forceUpdate ? " (forced)" : ""}...`, `Starting rankeds refreshing${forceUpdate ? " (forced)" : ""}...`,
"RankedsService", "RankedsService"
); );
try { try {
@ -50,9 +50,9 @@ export default () => {
if (lastUpdated && lastUpdated > new Date() - REFRESH_INTERVAL) { if (lastUpdated && lastUpdated > new Date() - REFRESH_INTERVAL) {
log.debug( log.debug(
`Refresh interval not yet expired, skipping. Next refresh on ${formatDate( `Refresh interval not yet expired, skipping. Next refresh on ${formatDate(
addToDate(REFRESH_INTERVAL, lastUpdated), addToDate(REFRESH_INTERVAL, lastUpdated)
)}`, )}`,
"RankedsService", "RankedsService"
); );
return null; return null;
@ -61,7 +61,7 @@ export default () => {
log.trace( log.trace(
`Fetching current rankeds from ScoreSaber...`, `Fetching current rankeds from ScoreSaber...`,
"RankedsService", "RankedsService"
); );
fetchedRankedSongs = await rankedsPageClient.getProcessed({ priority }); fetchedRankedSongs = await rankedsPageClient.getProcessed({ priority });
if (!fetchedRankedSongs || !fetchedRankedSongs.length) { if (!fetchedRankedSongs || !fetchedRankedSongs.length) {
@ -84,13 +84,13 @@ export default () => {
return { ...s, firstSeen, oldStars: null }; return { ...s, firstSeen, oldStars: null };
}), }),
"leaderboardId", "leaderboardId"
); );
// find differences between old and new ranked songs // find differences between old and new ranked songs
const newRankeds = arrayDifference( const newRankeds = arrayDifference(
Object.keys(fetchedRankedSongs), Object.keys(fetchedRankedSongs),
Object.keys(oldRankedSongs), Object.keys(oldRankedSongs)
).map((leaderboardId) => ({ ).map((leaderboardId) => ({
leaderboardId: parseInt(leaderboardId, 10), leaderboardId: parseInt(leaderboardId, 10),
oldStars: null, oldStars: null,
@ -110,20 +110,20 @@ export default () => {
s.stars !== s.stars !==
(fetchedRankedSongs[s.leaderboardId] (fetchedRankedSongs[s.leaderboardId]
? opt(fetchedRankedSongs[s.leaderboardId], "stars", null) ? opt(fetchedRankedSongs[s.leaderboardId], "stars", null)
: null), : null)
) )
.map((s) => ({ .map((s) => ({
leaderboardId: s.leaderboardId, leaderboardId: s.leaderboardId,
oldStars: s.stars, oldStars: s.stars,
stars: opt(fetchedRankedSongs[s.leaderboardId], "stars", null), stars: opt(fetchedRankedSongs[s.leaderboardId], "stars", null),
timestamp: Date.now(), timestamp: Date.now(),
})), }))
); );
if (newRankeds && changed && changed.length - newRankeds.length > 0) if (newRankeds && changed && changed.length - newRankeds.length > 0)
log.debug( log.debug(
`${changed.length - newRankeds.length} changed ranked(s) found`, `${changed.length - newRankeds.length} changed ranked(s) found`,
"RankedsService", "RankedsService"
); );
const changedLeaderboards = changed const changedLeaderboards = changed
@ -150,16 +150,16 @@ export default () => {
async (tx) => { async (tx) => {
await Promise.all( await Promise.all(
changedLeaderboards.map(async (ranked) => changedLeaderboards.map(async (ranked) =>
rankedsRepository().set(ranked, undefined, tx), rankedsRepository().set(ranked, undefined, tx)
), )
); );
await Promise.all( await Promise.all(
changed.map(async (rc) => changed.map(async (rc) =>
rankedsChangesRepository().set(rc, undefined, tx), rankedsChangesRepository().set(rc, undefined, tx)
), )
); );
await setLastUpdated(new Date()); await setLastUpdated(new Date());
}, }
); );
log.trace("Rankeds saved", "RankedsService"); log.trace("Rankeds saved", "RankedsService");

@ -15,17 +15,17 @@ export default () => {
const fetchGlobal = async ( const fetchGlobal = async (
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
signal = null, signal = null
) => ) =>
resolvePromiseOrWaitForPending(`apiClient/ranking/global/${page}`, () => resolvePromiseOrWaitForPending(`apiClient/ranking/global/${page}`, () =>
playersGlobalRankingApiClient.getProcessed({ page, signal, priority }), playersGlobalRankingApiClient.getProcessed({ page, signal, priority })
); );
const fetchCountry = async ( const fetchCountry = async (
country, country,
page = 1, page = 1,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
signal = null, signal = null
) => ) =>
resolvePromiseOrWaitForPending( resolvePromiseOrWaitForPending(
`pageClient/ranking/${country}/${page}`, `pageClient/ranking/${country}/${page}`,
@ -35,17 +35,17 @@ export default () => {
page, page,
signal, signal,
priority, priority,
}), })
); );
const fetchGlobalPages = async (priority = PRIORITY.FG_LOW, signal = null) => const fetchGlobalPages = async (priority = PRIORITY.FG_LOW, signal = null) =>
resolvePromiseOrWaitForPending(`apiClient/rankingGlobalPages`, () => resolvePromiseOrWaitForPending(`apiClient/rankingGlobalPages`, () =>
playersGlobalRankingPagesApiClient.getProcessed({ signal, priority }), playersGlobalRankingPagesApiClient.getProcessed({ signal, priority })
); );
const fetchGlobalCount = async ( const fetchGlobalCount = async (
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
signal = null, signal = null
) => { ) => {
const pages = await fetchGlobalPages(priority, signal); const pages = await fetchGlobalPages(priority, signal);
if (!pages || !Number.isFinite(pages)) return 0; if (!pages || !Number.isFinite(pages)) return 0;
@ -73,8 +73,8 @@ export default () => {
const ranking = ( const ranking = (
await Promise.all( await Promise.all(
pages.map(async (page) => pages.map(async (page) =>
country ? fetchCountry(country, page) : fetchGlobal(page), country ? fetchCountry(country, page) : fetchGlobal(page)
), )
) )
) )
.reduce((cum, arr) => cum.concat(arr), []) .reduce((cum, arr) => cum.concat(arr), [])

@ -118,7 +118,7 @@ export default () => {
const getPlayerScoresAsObject = async ( const getPlayerScoresAsObject = async (
playerId, playerId,
idFunc = (score) => opt(score, "leaderboard.leaderboardId"), idFunc = (score) => opt(score, "leaderboard.leaderboardId"),
asArray = false, asArray = false
) => convertScoresToObject(await getPlayerScores(playerId), idFunc, asArray); ) => convertScoresToObject(await getPlayerScores(playerId), idFunc, asArray);
const getPlayerSongScore = async (playerId, leaderboardId) => const getPlayerSongScore = async (playerId, leaderboardId) =>
scoresRepository().get(playerId + "_" + leaderboardId); scoresRepository().get(playerId + "_" + leaderboardId);
@ -137,7 +137,7 @@ export default () => {
scores.reduce((allScores, scorePage) => [...allScores, ...scorePage], []); scores.reduce((allScores, scorePage) => [...allScores, ...scorePage], []);
const isAnyScoreOlderThan = (scores, olderThan) => const isAnyScoreOlderThan = (scores, olderThan) =>
scores.some( scores.some(
(s) => s.score && s.score.timeSet && s.score.timeSet <= olderThan, (s) => s.score && s.score.timeSet && s.score.timeSet <= olderThan
); );
const createFetchUntilLastUpdated = (olderThan) => (scores) => const createFetchUntilLastUpdated = (olderThan) => (scores) =>
isAnyScoreOlderThan(scores, olderThan); isAnyScoreOlderThan(scores, olderThan);
@ -145,7 +145,7 @@ export default () => {
const convertScoresToObject = ( const convertScoresToObject = (
scores, scores,
idFunc = (score) => opt(score, "leaderboard.leaderboardId"), idFunc = (score) => opt(score, "leaderboard.leaderboardId"),
asArray = false, asArray = false
) => ) =>
scores.reduce((scoresObj, score) => { scores.reduce((scoresObj, score) => {
const _id = idFunc(score); const _id = idFunc(score);
@ -176,11 +176,11 @@ export default () => {
priority = PRIORITY.BG_NORMAL, priority = PRIORITY.BG_NORMAL,
signal = null, signal = null,
untilFunc = null, untilFunc = null,
dontReduce = false, dontReduce = false
) => { ) => {
log.debug( log.debug(
`Fetching scores of player "${playerId}" starting from page #${startPage}`, `Fetching scores of player "${playerId}" starting from page #${startPage}`,
"ScoresService", "ScoresService"
); );
let data = []; let data = [];
@ -212,7 +212,7 @@ export default () => {
) { ) {
// push only relevant scores and return // push only relevant scores and return
data.push( data.push(
pageData.filter((score) => !untilFunc || !untilFunc([score])), pageData.filter((score) => !untilFunc || !untilFunc([score]))
); );
break; break;
@ -238,11 +238,11 @@ export default () => {
playerId, playerId,
numOfPages, numOfPages,
priority = PRIORITY.BG_NORMAL, priority = PRIORITY.BG_NORMAL,
signal = null, signal = null
) => { ) => {
log.debug( log.debug(
`Fetching all scores of player "${playerId}, number of pages: ${numOfPages}`, `Fetching all scores of player "${playerId}, number of pages: ${numOfPages}`,
"ScoresService", "ScoresService"
); );
const pages = Array(numOfPages) const pages = Array(numOfPages)
@ -256,8 +256,8 @@ export default () => {
page, page,
signal, signal,
priority, priority,
}), })
), )
); );
if (!data || !data.length) return []; if (!data || !data.length) return [];
@ -271,7 +271,7 @@ export default () => {
priority, priority,
signal, signal,
null, null,
true, true
)), )),
]; ];
} }
@ -285,7 +285,7 @@ export default () => {
opt(s, "score.timeSet") && s.score.timeSet > recentPlay opt(s, "score.timeSet") && s.score.timeSet > recentPlay
? s.score.timeSet ? s.score.timeSet
: recentPlay, : recentPlay,
defaultRecentPlay, defaultRecentPlay
); );
const addScoreIndexFields = (playerId, score) => { const addScoreIndexFields = (playerId, score) => {
@ -310,12 +310,12 @@ export default () => {
try { try {
log.debug( log.debug(
"Rank and pp update queue, waiting for the scores to finish refreshing.", "Rank and pp update queue, waiting for the scores to finish refreshing.",
"ScoresService", "ScoresService"
); );
await refreshingFinished(); await refreshingFinished();
log.debug( log.debug(
"Rank and pp update queue, scores refreshed, start queue processing.", "Rank and pp update queue, scores refreshed, start queue processing.",
"ScoresService", "ScoresService"
); );
await db.runInTransaction( await db.runInTransaction(
@ -373,14 +373,14 @@ export default () => {
if (cursor) cursor = await cursor.continue(); if (cursor) cursor = await cursor.continue();
} }
} }
}, }
); );
log.debug("Rank and pp update queue processed.", "ScoresService"); log.debug("Rank and pp update queue processed.", "ScoresService");
} catch (err) { } catch (err) {
log.debug( log.debug(
"Rank and pp update queue has NOT been processed.", "Rank and pp update queue has NOT been processed.",
"ScoresService", "ScoresService"
); );
} }
}; };
@ -391,12 +391,12 @@ export default () => {
log.debug( log.debug(
"Queueing rank and pp update for bunch of scores", "Queueing rank and pp update for bunch of scores",
"ScoresService", "ScoresService",
scoresToUpdate, scoresToUpdate
); );
try { try {
await Promise.all( await Promise.all(
scoresToUpdate.map(async (s) => scoresUpdateQueueRepository().set(s)), scoresToUpdate.map(async (s) => scoresUpdateQueueRepository().set(s))
); );
} catch (err) { } catch (err) {
// swallow error // swallow error
@ -405,7 +405,7 @@ export default () => {
log.debug( log.debug(
"Scores rank & pp queued for update.", "Scores rank & pp queued for update.",
"ScoresService", "ScoresService",
scoresToUpdate, scoresToUpdate
); );
}; };
@ -414,7 +414,7 @@ export default () => {
log.warn( log.warn(
`Can not refresh scores, empty playerId`, `Can not refresh scores, empty playerId`,
"ScoresService", "ScoresService",
player, player
); );
return null; return null;
@ -434,7 +434,7 @@ export default () => {
const playerScores = await getPlayerScores(player.playerId); const playerScores = await getPlayerScores(player.playerId);
const currentScoresById = convertScoresById( const currentScoresById = convertScoresById(
player.playerId, player.playerId,
playerScores, playerScores
); );
let mostRecentPlayFromScores = null; let mostRecentPlayFromScores = null;
@ -455,7 +455,7 @@ export default () => {
player.playerId, player.playerId,
numOfPages, numOfPages,
priority, priority,
abortController.signal, abortController.signal
); );
else else
newScores = await fetchScoresUntil( newScores = await fetchScoresUntil(
@ -463,7 +463,7 @@ export default () => {
1, 1,
priority, priority,
abortController.signal, abortController.signal,
createFetchUntilLastUpdated(startUpdatingDate), createFetchUntilLastUpdated(startUpdatingDate)
); );
if (!newScores || !newScores.length) { if (!newScores || !newScores.length) {
@ -568,7 +568,7 @@ export default () => {
const getScoresFreshnessDate = ( const getScoresFreshnessDate = (
player, player,
refreshInterval = null, refreshInterval = null,
key = "scoresLastUpdated", key = "scoresLastUpdated"
) => { ) => {
const lastUpdated = player && player[key] ? player[key] : null; const lastUpdated = player && player[key] ? player[key] : null;
if (!lastUpdated) return addToDate(-SECOND); if (!lastUpdated) return addToDate(-SECOND);
@ -585,12 +585,12 @@ export default () => {
const isScoreDateFresh = ( const isScoreDateFresh = (
player, player,
refreshInterval = null, refreshInterval = null,
key = "scoresLastUpdated", key = "scoresLastUpdated"
) => getScoresFreshnessDate(player, refreshInterval, key) > new Date(); ) => getScoresFreshnessDate(player, refreshInterval, key) > new Date();
const getPlayerScoresPage = async ( const getPlayerScoresPage = async (
playerId, playerId,
serviceParams = { sort: "recent", order: "desc", page: 1 }, serviceParams = { sort: "recent", order: "desc", page: 1 }
) => { ) => {
let page = serviceParams?.page ?? 1; let page = serviceParams?.page ?? 1;
if (page < 1) page = 1; if (page < 1) page = 1;
@ -615,7 +615,7 @@ export default () => {
}; };
const getScoresHistogramDefinition = ( const getScoresHistogramDefinition = (
serviceParams = { sort: "recent", order: "desc" }, serviceParams = { sort: "recent", order: "desc" }
) => { ) => {
const sort = serviceParams?.sort ?? "recent"; const sort = serviceParams?.sort ?? "recent";
const order = serviceParams?.order ?? "desc"; const order = serviceParams?.order ?? "desc";
@ -732,7 +732,7 @@ export default () => {
playerId, playerId,
serviceParams = { sort: "recent", order: "desc", page: 1 }, serviceParams = { sort: "recent", order: "desc", page: 1 },
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
{ ...options } = {}, { ...options } = {}
) => ) =>
((serviceParams?.sort ?? "recent") === "top" ((serviceParams?.sort ?? "recent") === "top"
? topScoresApiClient ? topScoresApiClient
@ -750,7 +750,7 @@ export default () => {
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
signal = null, signal = null,
canUseBrowserCache = false, canUseBrowserCache = false,
refreshInterval = MINUTE, refreshInterval = MINUTE
) => { ) => {
const fetchedScoresResponse = await fetchScoresPage( const fetchedScoresResponse = await fetchScoresPage(
playerId, playerId,
@ -761,13 +761,13 @@ export default () => {
cacheTtl: MINUTE, cacheTtl: MINUTE,
maxAge: canUseBrowserCache ? 0 : refreshInterval, maxAge: canUseBrowserCache ? 0 : refreshInterval,
fullResponse: true, fullResponse: true,
}, }
); );
if (topScoresApiClient.isResponseCached(fetchedScoresResponse)) if (topScoresApiClient.isResponseCached(fetchedScoresResponse))
return topScoresApiClient.getDataFromResponse(fetchedScoresResponse); return topScoresApiClient.getDataFromResponse(fetchedScoresResponse);
const fetchedScores = topScoresApiClient.getDataFromResponse( const fetchedScores = topScoresApiClient.getDataFromResponse(
fetchedScoresResponse, fetchedScoresResponse
); );
const playerScores = await getPlayerScores(playerId); const playerScores = await getPlayerScores(playerId);
@ -835,7 +835,7 @@ export default () => {
refreshInterval = MINUTE, refreshInterval = MINUTE,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
signal = null, signal = null,
force = false, force = false
) => { ) => {
if (!player || !player.playerId) return null; if (!player || !player.playerId) return null;
@ -845,7 +845,7 @@ export default () => {
const scoresPage = await getPlayerScoresPage( const scoresPage = await getPlayerScoresPage(
player.playerId, player.playerId,
serviceParams, serviceParams
); );
if ( if (
@ -889,7 +889,7 @@ export default () => {
priority, priority,
signal, signal,
canUseBrowserCache && !shouldPageBeRefetched, canUseBrowserCache && !shouldPageBeRefetched,
refreshInterval, refreshInterval
); );
return scoresPage; return scoresPage;
@ -899,7 +899,7 @@ export default () => {
playerId, playerId,
forceUpdate = false, forceUpdate = false,
priority = PRIORITY.BG_NORMAL, priority = PRIORITY.BG_NORMAL,
throwErrors = false, throwErrors = false
) => { ) => {
refreshCallCounter++; refreshCallCounter++;
@ -908,13 +908,13 @@ export default () => {
`Starting player "${playerId}" scores refreshing${ `Starting player "${playerId}" scores refreshing${
forceUpdate ? " (forced)" : "" forceUpdate ? " (forced)" : ""
}...`, }...`,
"ScoresService", "ScoresService"
); );
if (!playerId) { if (!playerId) {
log.warn( log.warn(
`Can not refresh player scores if an empty playerId is given`, `Can not refresh player scores if an empty playerId is given`,
"ScoresService", "ScoresService"
); );
return null; return null;
@ -923,7 +923,7 @@ export default () => {
if (updateInProgress.includes(playerId)) { if (updateInProgress.includes(playerId)) {
log.warn( log.warn(
`Player "${playerId}" scores are being fetched, skipping.`, `Player "${playerId}" scores are being fetched, skipping.`,
"ScoresService", "ScoresService"
); );
return null; return null;
@ -939,7 +939,7 @@ export default () => {
if (!player) { if (!player) {
log.debug( log.debug(
`Can not refresh the scores of player "${playerId}" because it has not been added to the DB`, `Can not refresh the scores of player "${playerId}" because it has not been added to the DB`
); );
return null; return null;
} }
@ -949,9 +949,9 @@ export default () => {
if (scoresFreshnessDate > new Date()) { if (scoresFreshnessDate > new Date()) {
log.debug( log.debug(
`Player "${playerId}" scores are still fresh, skipping. Next refresh on ${formatDate( `Player "${playerId}" scores are still fresh, skipping. Next refresh on ${formatDate(
scoresFreshnessDate, scoresFreshnessDate
)}`, )}`,
"ScoresService", "ScoresService"
); );
return { return {
@ -959,7 +959,7 @@ export default () => {
newScores: null, newScores: null,
scores: convertScoresById( scores: convertScoresById(
player.playerId, player.playerId,
await getPlayerScores(player.playerId), await getPlayerScores(player.playerId)
), ),
}; };
} }
@ -967,18 +967,18 @@ export default () => {
log.trace( log.trace(
`Fetching player "${playerId}" scores from ScoreSaber...`, `Fetching player "${playerId}" scores from ScoreSaber...`,
"ScoresService", "ScoresService"
); );
const updatedPlayerScores = await resolvePromiseOrWaitForPending( const updatedPlayerScores = await resolvePromiseOrWaitForPending(
`service/updatePlayerScores/${playerId}`, `service/updatePlayerScores/${playerId}`,
() => updatePlayerScores(player, priority), () => updatePlayerScores(player, priority)
); );
if (!updatedPlayerScores) { if (!updatedPlayerScores) {
log.warn( log.warn(
`Can not refresh player "${playerId}" scores`, `Can not refresh player "${playerId}" scores`,
"ScoresService", "ScoresService"
); );
return null; return null;
@ -989,7 +989,7 @@ export default () => {
log.trace( log.trace(
`Player "${playerId}" scores updated`, `Player "${playerId}" scores updated`,
"ScoresService", "ScoresService",
scoresInfo.newScores, scoresInfo.newScores
); );
if (scoresInfo.newScores && scoresInfo.newScores.length) { if (scoresInfo.newScores && scoresInfo.newScores.length) {
@ -1012,7 +1012,7 @@ export default () => {
e.toString ? `: ${e.toString()}` : "" e.toString ? `: ${e.toString()}` : ""
}`, }`,
"ScoresService", "ScoresService",
e, e
); );
return null; return null;
@ -1026,11 +1026,11 @@ export default () => {
const refreshAll = async ( const refreshAll = async (
force = false, force = false,
priority = PRIORITY.BG_NORMAL, priority = PRIORITY.BG_NORMAL,
throwErrors = false, throwErrors = false
) => { ) => {
log.trace( log.trace(
`Starting refreshing all players scores${force ? " (forced)" : ""}...`, `Starting refreshing all players scores${force ? " (forced)" : ""}...`,
"ScoresService", "ScoresService"
); );
const allActivePlayers = await playerService.getAllActive(); const allActivePlayers = await playerService.getAllActive();
@ -1041,19 +1041,19 @@ export default () => {
const allNewScores = await Promise.all( const allNewScores = await Promise.all(
allActivePlayers.map((player) => allActivePlayers.map((player) =>
refresh(player.playerId, force, priority, throwErrors), refresh(player.playerId, force, priority, throwErrors)
), )
); );
const allPlayersWithNewScores = allActivePlayers.map((player, idx) => const allPlayersWithNewScores = allActivePlayers.map((player, idx) =>
allNewScores[idx] allNewScores[idx]
? { player, ...allNewScores[idx] } ? { player, ...allNewScores[idx] }
: { player, newScores: null, scores: null, recentPlay: null }, : { player, newScores: null, scores: null, recentPlay: null }
); );
log.trace( log.trace(
`All players scores refreshed.`, `All players scores refreshed.`,
"ScoresService", "ScoresService",
allPlayersWithNewScores, allPlayersWithNewScores
); );
return allPlayersWithNewScores; return allPlayersWithNewScores;

@ -87,7 +87,7 @@ export default () => {
const fetchProfile = async ( const fetchProfile = async (
login, login,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
{ fullResponse = false, ...options } = {}, { fullResponse = false, ...options } = {}
) => { ) => {
const token = await getCurrentToken(); const token = await getCurrentToken();
if (!token || !token.expires || token.expires <= new Date()) return null; if (!token || !token.expires || token.expires <= new Date()) return null;
@ -101,14 +101,14 @@ export default () => {
login, login,
priority, priority,
fullResponse, fullResponse,
}), })
); );
}; };
const fetchVideos = async ( const fetchVideos = async (
userId, userId,
priority = PRIORITY.FG_LOW, priority = PRIORITY.FG_LOW,
{ fullResponse = false, ...options } = {}, { fullResponse = false, ...options } = {}
) => { ) => {
const token = await getCurrentToken(); const token = await getCurrentToken();
if (!token || !token.expires || token.expires <= new Date()) return null; if (!token || !token.expires || token.expires <= new Date()) return null;
@ -122,7 +122,7 @@ export default () => {
userId, userId,
priority, priority,
fullResponse, fullResponse,
}), })
); );
}; };
@ -134,11 +134,11 @@ export default () => {
playerId, playerId,
forceUpdate = false, forceUpdate = false,
priority = queues.PRIORITY.FG_LOW, priority = queues.PRIORITY.FG_LOW,
throwErrors = false, throwErrors = false
) => { ) => {
log.trace( log.trace(
`Starting Twitch videos refreshing${forceUpdate ? " (forced)" : ""}...`, `Starting Twitch videos refreshing${forceUpdate ? " (forced)" : ""}...`,
"TwitchService", "TwitchService"
); );
if (!playerId) { if (!playerId) {
@ -152,7 +152,7 @@ export default () => {
if (!twitchProfile || !twitchProfile.login) { if (!twitchProfile || !twitchProfile.login) {
log.debug( log.debug(
`Twitch profile for player ${playerId} is not set, skipping`, `Twitch profile for player ${playerId} is not set, skipping`,
"TwitchService", "TwitchService"
); );
return null; return null;
@ -163,9 +163,9 @@ export default () => {
if (lastUpdated && lastUpdated > new Date() - REFRESH_INTERVAL) { if (lastUpdated && lastUpdated > new Date() - REFRESH_INTERVAL) {
log.debug( log.debug(
`Refresh interval not yet expired, skipping. Next refresh on ${formatDate( `Refresh interval not yet expired, skipping. Next refresh on ${formatDate(
addToDate(REFRESH_INTERVAL, lastUpdated), addToDate(REFRESH_INTERVAL, lastUpdated)
)}`, )}`,
"TwitchService", "TwitchService"
); );
return twitchProfile; return twitchProfile;
@ -177,7 +177,7 @@ export default () => {
if (lastUpdated && lastUpdated > player.recentPlay) { if (lastUpdated && lastUpdated > player.recentPlay) {
log.debug( log.debug(
`Twitch updated after recent player play, skipping`, `Twitch updated after recent player play, skipping`,
"TwitchService", "TwitchService"
); );
return twitchProfile; return twitchProfile;
@ -189,7 +189,7 @@ export default () => {
if (!fetchedProfile) { if (!fetchedProfile) {
log.debug( log.debug(
`Can not fetch Twitch profile for player ${playerId}, skipping`, `Can not fetch Twitch profile for player ${playerId}, skipping`,
"TwitchService", "TwitchService"
); );
return twitchProfile; return twitchProfile;
@ -220,7 +220,7 @@ export default () => {
log.debug( log.debug(
`Twitch player ${playerId} refreshing error`, `Twitch player ${playerId} refreshing error`,
"TwitchService", "TwitchService",
e, e
); );
return null; return null;
@ -243,7 +243,7 @@ export default () => {
created_at: dateFromString(v.created_at), created_at: dateFromString(v.created_at),
ended_at: addToDate( ended_at: addToDate(
durationToMillis(v.duration), durationToMillis(v.duration),
dateFromString(v.created_at), dateFromString(v.created_at)
), ),
})) }))
.find((v) => v.created_at <= songStarted && songStarted < v.ended_at); .find((v) => v.created_at <= songStarted && songStarted < v.ended_at);

@ -5,7 +5,7 @@ export const getServicePlayerGain = (
dateTruncFunc, dateTruncFunc,
dateKey, dateKey,
daysAgo = 1, daysAgo = 1,
maxDaysAgo = 7, maxDaysAgo = 7
) => { ) => {
if (!playerHistory?.length) return null; if (!playerHistory?.length) return null;
@ -21,7 +21,7 @@ export const getServicePlayerGain = (
return playerHistory return playerHistory
.sort((a, b) => b?.[dateKey]?.getTime() - a?.[dateKey]?.getTime()) .sort((a, b) => b?.[dateKey]?.getTime() - a?.[dateKey]?.getTime())
.find( .find(
(h) => h?.[dateKey] <= compareToDate && h?.[dateKey] >= maxServiceDate, (h) => h?.[dateKey] <= compareToDate && h?.[dateKey] >= maxServiceDate
); );
}; };

@ -78,7 +78,7 @@ export default async () => {
const determineNewSettingsAvailable = (dbConfig) => const determineNewSettingsAvailable = (dbConfig) =>
Object.entries(newSettingsAvailableDefinition) Object.entries(newSettingsAvailableDefinition)
.map(([key, description]) => .map(([key, description]) =>
opt(dbConfig, key) === undefined ? description : null, opt(dbConfig, key) === undefined ? description : null
) )
.filter((d) => d); .filter((d) => d);

@ -1,7 +1,7 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
export default ( export default (
sizes = { phone: 0, tablet: 768, desktop: 1024, xxl: 1749 }, sizes = { phone: 0, tablet: 768, desktop: 1024, xxl: 1749 }
) => { ) => {
const defaultValue = { name: null, width: null, nodeWidth: null, rect: null }; const defaultValue = { name: null, width: null, nodeWidth: null, rect: null };
const { subscribe, unsubscribe, set } = writable(defaultValue); const { subscribe, unsubscribe, set } = writable(defaultValue);
@ -41,8 +41,8 @@ export default (
item[1] <= nodeWidth item[1] <= nodeWidth
? { name: item[0], width: item[1], nodeWidth, rect } ? { name: item[0], width: item[1], nodeWidth, rect }
: cum, : cum,
defaultValue, defaultValue
), )
); );
}); });

@ -11,7 +11,7 @@ export default (score, bmStats, leaderboardId) => {
} else if (leaderboardId) { } else if (leaderboardId) {
maxScore = getFixedLeaderboardMaxScore( maxScore = getFixedLeaderboardMaxScore(
leaderboardId, leaderboardId,
score?.maxScore ?? null, score?.maxScore ?? null
); );
} }

@ -9,6 +9,6 @@ export default async (data, cachedOnly = false) => {
data.leaderboard.beatMaps = await beatMaps.byHash( data.leaderboard.beatMaps = await beatMaps.byHash(
data.leaderboard.song.hash, data.leaderboard.song.hash,
false, false,
cachedOnly, cachedOnly
); );
}; };

@ -15,7 +15,7 @@ export default async (data) => {
: 0; : 0;
const bmStats = findDiffInfoWithDiffAndTypeFromBeatMaps( const bmStats = findDiffInfoWithDiffAndTypeFromBeatMaps(
opt(data, `leaderboard.beatMaps.versions.${versionsLastIdx}.diffs`), opt(data, `leaderboard.beatMaps.versions.${versionsLastIdx}.diffs`),
diffInfo, diffInfo
); );
data.score = calculateAcc(data.score, bmStats, leaderboardId); data.score = calculateAcc(data.score, bmStats, leaderboardId);

@ -48,11 +48,11 @@ export default async (data, playerId = null) => {
const mainPlayerScore = await produce( const mainPlayerScore = await produce(
await produce( await produce(
await produce(comparePlayerScores[leaderboardId], (draft) => await produce(comparePlayerScores[leaderboardId], (draft) =>
beatMapsEnhancer(draft), beatMapsEnhancer(draft)
), ),
(draft) => accEnhancer(draft, true), (draft) => accEnhancer(draft, true)
), ),
(draft) => beatSaviorEnhancer(draft, mainPlayerId), (draft) => beatSaviorEnhancer(draft, mainPlayerId)
); );
data.comparePlayers = [ data.comparePlayers = [

@ -15,7 +15,7 @@ export default async (data, playerId = null) => {
if (!scoresService) scoresService = createScoresService(); if (!scoresService) scoresService = createScoresService();
const playerScores = scoresService.convertScoresToObject( const playerScores = scoresService.convertScoresToObject(
await scoresService.getPlayerScores(playerId), await scoresService.getPlayerScores(playerId)
); );
// skip if no cached score // skip if no cached score
@ -41,7 +41,7 @@ export default async (data, playerId = null) => {
: 0; : 0;
const bmStats = findDiffInfoWithDiffAndTypeFromBeatMaps( const bmStats = findDiffInfoWithDiffAndTypeFromBeatMaps(
opt(data, `leaderboard.beatMaps.versions.${versionsLastIdx}.diffs`), opt(data, `leaderboard.beatMaps.versions.${versionsLastIdx}.diffs`),
diffInfo, diffInfo
); );
data.prevScore = calculateAcc(prevScore, bmStats, leaderboardId); data.prevScore = calculateAcc(prevScore, bmStats, leaderboardId);

@ -20,7 +20,7 @@ export default async (data, playerId = null, whatIfOnly = false) => {
const whatIfPp = await ppService.getWhatIfScore( const whatIfPp = await ppService.getWhatIfScore(
mainPlayerId, mainPlayerId,
leaderboardId, leaderboardId,
pp, pp
); );
if (whatIfPp && whatIfPp.diff >= 0.01) data.score.whatIfPp = whatIfPp; if (whatIfPp && whatIfPp.diff >= 0.01) data.score.whatIfPp = whatIfPp;
} }

@ -15,7 +15,7 @@ export default async (data, playerId = null) => {
: 0; : 0;
const bmStats = findDiffInfoWithDiffAndTypeFromBeatMaps( const bmStats = findDiffInfoWithDiffAndTypeFromBeatMaps(
opt(data, `leaderboard.beatMaps.versions.${versionsLastIdx}.diffs`), opt(data, `leaderboard.beatMaps.versions.${versionsLastIdx}.diffs`),
data.leaderboard.diffInfo, data.leaderboard.diffInfo
); );
if (!bmStats || !bmStats.seconds) return; if (!bmStats || !bmStats.seconds) return;
@ -27,7 +27,7 @@ export default async (data, playerId = null) => {
const video = await twitchService.findTwitchVideo( const video = await twitchService.findTwitchVideo(
twitchProfile, twitchProfile,
data.score.timeSet, data.score.timeSet,
bmStats.seconds, bmStats.seconds
); );
if (!video || !video.url) return; if (!video || !video.url) return;

@ -13,7 +13,7 @@ export default (
type = "global", type = "global",
page = 1, page = 1,
initialState = null, initialState = null,
initialStateType = "initial", initialStateType = "initial"
) => { ) => {
let currentLeaderboardId = leaderboardId ? leaderboardId : null; let currentLeaderboardId = leaderboardId ? leaderboardId : null;
let currentType = type ? type : "global"; let currentType = type ? type : "global";
@ -62,13 +62,13 @@ export default (
const patchId = getPatchId(currentLeaderboardId, scoreRow); const patchId = getPatchId(currentLeaderboardId, scoreRow);
const stateRowIdx = newState.scores.findIndex( const stateRowIdx = newState.scores.findIndex(
(s) => getPatchId(currentLeaderboardId, s) === patchId, (s) => getPatchId(currentLeaderboardId, s) === patchId
); );
if (stateRowIdx < 0) return; if (stateRowIdx < 0) return;
newState.scores[stateRowIdx] = applyPatches( newState.scores[stateRowIdx] = applyPatches(
newState.scores[stateRowIdx], newState.scores[stateRowIdx],
enhancePatches[patchId], enhancePatches[patchId]
); );
debouncedSetState(enhanceTaskId, newState); debouncedSetState(enhanceTaskId, newState);
@ -88,7 +88,7 @@ export default (
const bpm = newState?.leaderboard?.beatMaps?.metadata?.bpm ?? null; const bpm = newState?.leaderboard?.beatMaps?.metadata?.bpm ?? null;
const bmStats = findDiffInfoWithDiffAndTypeFromBeatMaps( const bmStats = findDiffInfoWithDiffAndTypeFromBeatMaps(
newState?.leaderboard?.beatMaps?.versions?.[versionsLastIdx]?.diffs, newState?.leaderboard?.beatMaps?.versions?.[versionsLastIdx]?.diffs,
newState?.leaderboard?.diffInfo, newState?.leaderboard?.diffInfo
); );
if (!bmStats) return null; if (!bmStats) return null;
@ -113,7 +113,7 @@ export default (
leaderboard: newState.leaderboard, leaderboard: newState.leaderboard,
}, },
getPatchId(currentLeaderboardId, scoreRow), getPatchId(currentLeaderboardId, scoreRow),
(draft) => accEnhancer(draft), (draft) => accEnhancer(draft)
) )
.then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)) .then((scoreRow) => setStateRow(enhanceTaskId, scoreRow))
.then((scoreRow) => .then((scoreRow) =>
@ -124,9 +124,9 @@ export default (
ppAttributionEnhancer( ppAttributionEnhancer(
draft, draft,
scoreRow?.player?.playerId, scoreRow?.player?.playerId,
true, true
), )
), )
) )
.then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)); .then((scoreRow) => setStateRow(enhanceTaskId, scoreRow));
} }
@ -144,14 +144,14 @@ export default (
onAfterStateChange: onNewData, onAfterStateChange: onNewData,
onSetPending: ({ fetchParams }) => ({ ...fetchParams }), onSetPending: ({ fetchParams }) => ({ ...fetchParams }),
}, },
initialStateType, initialStateType
); );
const fetch = async ( const fetch = async (
leaderboardId = currentLeaderboardId, leaderboardId = currentLeaderboardId,
type = currentType, type = currentType,
page = currentPage, page = currentPage,
force = false, force = false
) => { ) => {
if (!leaderboardId) return false; if (!leaderboardId) return false;

@ -4,7 +4,7 @@ import playerApiClient from "../../network/clients/scoresaber/player/api";
export default ( export default (
playerId = null, playerId = null,
initialState = null, initialState = null,
initialStateType = "initial", initialStateType = "initial"
) => { ) => {
let currentPlayerId = playerId; let currentPlayerId = playerId;
@ -20,7 +20,7 @@ export default (
onInitialized: onNewData, onInitialized: onNewData,
onAfterStateChange: onNewData, onAfterStateChange: onNewData,
}, },
initialStateType, initialStateType
); );
const fetch = async (playerId = currentPlayerId, force = false) => { const fetch = async (playerId = currentPlayerId, force = false) => {

@ -11,7 +11,7 @@ export default (
service = "scoresaber", service = "scoresaber",
serviceParams = { type: "recent", page: 1 }, serviceParams = { type: "recent", page: 1 },
initialState = null, initialState = null,
initialStateType = "initial", initialStateType = "initial"
) => { ) => {
let currentPlayerId = playerId; let currentPlayerId = playerId;
let currentService = service; let currentService = service;
@ -42,14 +42,14 @@ export default (
onInitialized: onNewData, onInitialized: onNewData,
onAfterStateChange: onNewData, onAfterStateChange: onNewData,
}, },
initialStateType, initialStateType
); );
const fetch = async ( const fetch = async (
playerId = currentPlayerId, playerId = currentPlayerId,
service = currentService, service = currentService,
serviceParams = currentServiceParams, serviceParams = currentServiceParams,
force = false, force = false
) => { ) => {
if ( if (
(!playerId || playerId === currentPlayerId) && (!playerId || playerId === currentPlayerId) &&
@ -70,7 +70,7 @@ export default (
{ playerId, service, serviceParams }, { playerId, service, serviceParams },
force, force,
provider, provider,
!playerId || playerId !== currentPlayerId || force, !playerId || playerId !== currentPlayerId || force
); );
}; };
@ -94,7 +94,7 @@ export default (
playerForLastRecentPlay = playerId; playerForLastRecentPlay = playerId;
await refresh(); await refresh();
}, }
); );
const subscribe = (fn) => { const subscribe = (fn) => {
@ -112,7 +112,7 @@ export default (
if (!currentPlayerId) { if (!currentPlayerId) {
setTimeout( setTimeout(
() => enqueueRecentPlayRefresh(), () => enqueueRecentPlayRefresh(),
DEFAULT_RECENT_PLAY_REFRESH_INTERVAL, DEFAULT_RECENT_PLAY_REFRESH_INTERVAL
); );
return; return;
@ -130,7 +130,7 @@ export default (
setTimeout( setTimeout(
() => enqueueRecentPlayRefresh(), () => enqueueRecentPlayRefresh(),
DEFAULT_RECENT_PLAY_REFRESH_INTERVAL, DEFAULT_RECENT_PLAY_REFRESH_INTERVAL
); );
return { return {

@ -5,7 +5,7 @@ export default (
type = "global", type = "global",
page = 1, page = 1,
initialState = null, initialState = null,
initialStateType = "initial", initialStateType = "initial"
) => { ) => {
let currentType = type ? type : "global"; let currentType = type ? type : "global";
let currentPage = page ? page : 1; let currentPage = page ? page : 1;
@ -26,13 +26,13 @@ export default (
onAfterStateChange: onNewData, onAfterStateChange: onNewData,
onSetPending: ({ fetchParams }) => ({ ...fetchParams }), onSetPending: ({ fetchParams }) => ({ ...fetchParams }),
}, },
initialStateType, initialStateType
); );
const fetch = async ( const fetch = async (
type = currentType, type = currentType,
page = currentPage, page = currentPage,
force = false, force = false
) => { ) => {
if ( if (
(!type || type === currentType) && (!type || type === currentType) &&

@ -17,7 +17,7 @@ export default (
service = "scoresaber", service = "scoresaber",
serviceParams = { type: "recent", page: 1 }, serviceParams = { type: "recent", page: 1 },
initialState = null, initialState = null,
initialStateType = "initial", initialStateType = "initial"
) => { ) => {
let currentPlayerId = playerId; let currentPlayerId = playerId;
let currentService = service; let currentService = service;
@ -82,13 +82,13 @@ export default (
const patchId = getPatchId(currentPlayerId, scoreRow); const patchId = getPatchId(currentPlayerId, scoreRow);
const stateRowIdx = newState.findIndex( const stateRowIdx = newState.findIndex(
(s) => getPatchId(currentPlayerId, s) === patchId, (s) => getPatchId(currentPlayerId, s) === patchId
); );
if (stateRowIdx < 0) return; if (stateRowIdx < 0) return;
newState[stateRowIdx] = applyPatches( newState[stateRowIdx] = applyPatches(
newState[stateRowIdx], newState[stateRowIdx],
enhancePatches[patchId], enhancePatches[patchId]
); );
debouncedSetState(enhanceTaskId, newState); debouncedSetState(enhanceTaskId, newState);
@ -99,74 +99,74 @@ export default (
for (const scoreRow of newState) { for (const scoreRow of newState) {
if (currentService !== "accsaber") { if (currentService !== "accsaber") {
stateProduce(scoreRow, getPatchId(currentPlayerId, scoreRow), (draft) => stateProduce(scoreRow, getPatchId(currentPlayerId, scoreRow), (draft) =>
beatMapsEnhancer(draft), beatMapsEnhancer(draft)
) )
.then((scoreRow) => .then((scoreRow) =>
stateProduce( stateProduce(
scoreRow, scoreRow,
getPatchId(currentPlayerId, scoreRow), getPatchId(currentPlayerId, scoreRow),
(draft) => accEnhancer(draft), (draft) => accEnhancer(draft)
), )
) )
.then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)) .then((scoreRow) => setStateRow(enhanceTaskId, scoreRow))
.then((scoreRow) => .then((scoreRow) =>
stateProduce( stateProduce(
scoreRow, scoreRow,
getPatchId(currentPlayerId, scoreRow), getPatchId(currentPlayerId, scoreRow),
(draft) => diffEnhancer(draft, currentPlayerId), (draft) => diffEnhancer(draft, currentPlayerId)
), )
) )
.then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)) .then((scoreRow) => setStateRow(enhanceTaskId, scoreRow))
.then((scoreRow) => .then((scoreRow) =>
stateProduce( stateProduce(
scoreRow, scoreRow,
getPatchId(currentPlayerId, scoreRow), getPatchId(currentPlayerId, scoreRow),
(draft) => compareEnhancer(draft, currentPlayerId), (draft) => compareEnhancer(draft, currentPlayerId)
), )
) )
.then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)) .then((scoreRow) => setStateRow(enhanceTaskId, scoreRow))
.then((scoreRow) => .then((scoreRow) =>
stateProduce( stateProduce(
scoreRow, scoreRow,
getPatchId(currentPlayerId, scoreRow), getPatchId(currentPlayerId, scoreRow),
(draft) => twitchEnhancer(draft, currentPlayerId), (draft) => twitchEnhancer(draft, currentPlayerId)
), )
) )
.then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)); .then((scoreRow) => setStateRow(enhanceTaskId, scoreRow));
stateProduce(scoreRow, getPatchId(currentPlayerId, scoreRow), (draft) => stateProduce(scoreRow, getPatchId(currentPlayerId, scoreRow), (draft) =>
rankedsEnhancer(draft), rankedsEnhancer(draft)
).then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)); ).then((scoreRow) => setStateRow(enhanceTaskId, scoreRow));
stateProduce(scoreRow, getPatchId(currentPlayerId, scoreRow), (draft) => stateProduce(scoreRow, getPatchId(currentPlayerId, scoreRow), (draft) =>
ppAttributionEnhancer(draft, currentPlayerId), ppAttributionEnhancer(draft, currentPlayerId)
).then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)); ).then((scoreRow) => setStateRow(enhanceTaskId, scoreRow));
if (stateType && stateType === "live") if (stateType && stateType === "live")
stateProduce( stateProduce(
scoreRow, scoreRow,
getPatchId(currentPlayerId, scoreRow), getPatchId(currentPlayerId, scoreRow),
(draft) => beatSaviorEnhancer(draft, currentPlayerId), (draft) => beatSaviorEnhancer(draft, currentPlayerId)
).then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)); ).then((scoreRow) => setStateRow(enhanceTaskId, scoreRow));
} else { } else {
stateProduce(scoreRow, getPatchId(currentPlayerId, scoreRow), (draft) => stateProduce(scoreRow, getPatchId(currentPlayerId, scoreRow), (draft) =>
beatMapsEnhancer(draft), beatMapsEnhancer(draft)
) )
.then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)) .then((scoreRow) => setStateRow(enhanceTaskId, scoreRow))
.then((scoreRow) => .then((scoreRow) =>
stateProduce( stateProduce(
scoreRow, scoreRow,
getPatchId(currentPlayerId, scoreRow), getPatchId(currentPlayerId, scoreRow),
(draft) => twitchEnhancer(draft, currentPlayerId), (draft) => twitchEnhancer(draft, currentPlayerId)
), )
) )
.then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)) .then((scoreRow) => setStateRow(enhanceTaskId, scoreRow))
.then((scoreRow) => .then((scoreRow) =>
stateProduce( stateProduce(
scoreRow, scoreRow,
getPatchId(currentPlayerId, scoreRow), getPatchId(currentPlayerId, scoreRow),
(draft) => beatSaviorEnhancer(draft, currentPlayerId), (draft) => beatSaviorEnhancer(draft, currentPlayerId)
), )
) )
.then((scoreRow) => setStateRow(enhanceTaskId, scoreRow)); .then((scoreRow) => setStateRow(enhanceTaskId, scoreRow));
} }
@ -185,14 +185,14 @@ export default (
onAfterStateChange: onNewData, onAfterStateChange: onNewData,
onSetPending: ({ fetchParams }) => ({ ...fetchParams }), onSetPending: ({ fetchParams }) => ({ ...fetchParams }),
}, },
initialStateType, initialStateType
); );
const fetch = async ( const fetch = async (
serviceParams = currentServiceParams, serviceParams = currentServiceParams,
service = currentService, service = currentService,
playerId = currentPlayerId, playerId = currentPlayerId,
force = false, force = false
) => { ) => {
if ( if (
(!playerId || playerId === currentPlayerId) && (!playerId || playerId === currentPlayerId) &&
@ -207,7 +207,7 @@ export default (
{ playerId, service, serviceParams }, { playerId, service, serviceParams },
force, force,
provider, provider,
!playerId || playerId !== currentPlayerId || force, !playerId || playerId !== currentPlayerId || force
); );
}; };

@ -16,7 +16,7 @@ export default (
onSetPending = null, onSetPending = null,
onError = null, onError = null,
} = {}, } = {},
initialStateType = "initial", initialStateType = "initial"
) => { ) => {
const getFinalParams = (fetchParams) => ({ const getFinalParams = (fetchParams) => ({
...defaultFetchParams, ...defaultFetchParams,
@ -46,7 +46,7 @@ export default (
fetchParams = {}, fetchParams = {},
force = false, force = false,
provider = currentProvider, provider = currentProvider,
fetchCachedFirst = false, fetchCachedFirst = false
) => { ) => {
const abortController = new AbortController(); const abortController = new AbortController();
@ -67,7 +67,7 @@ export default (
set( set(
onBeforeStateChange onBeforeStateChange
? onBeforeStateChange(cachedState, stateType) ? onBeforeStateChange(cachedState, stateType)
: cachedState, : cachedState
); );
} }
}); });
@ -78,7 +78,7 @@ export default (
setPending( setPending(
onSetPending onSetPending
? onSetPending({ fetchParams, abortController }) ? onSetPending({ fetchParams, abortController })
: fetchParams, : fetchParams
); );
pendingAbortController = abortController; pendingAbortController = abortController;

@ -30,14 +30,14 @@ export default () => {
refreshInterval, refreshInterval,
priority, priority,
signal, signal,
force, force
); );
const scores = await scoresFetcher.fetchLiveScores( const scores = await scoresFetcher.fetchLiveScores(
player, player,
service, service,
serviceParams, serviceParams,
{ refreshInterval, priority, signal, force }, { refreshInterval, priority, signal, force }
); );
return { ...player, scores, service, serviceParams }; return { ...player, scores, service, serviceParams };

@ -20,7 +20,7 @@ export default () => {
return; return;
player = newPlayer; player = newPlayer;
}, }
); );
const playerRecentPlayUpdatedUnsubscribe = eventBus.on( const playerRecentPlayUpdatedUnsubscribe = eventBus.on(
@ -30,7 +30,7 @@ export default () => {
player.recentPlay = recentPlay; player.recentPlay = recentPlay;
player.recentPlayLastUpdated = recentPlayLastUpdated; player.recentPlayLastUpdated = recentPlayLastUpdated;
}, }
); );
return { return {
@ -47,7 +47,7 @@ export default () => {
playerId, playerId,
MINUTE, MINUTE,
priority, priority,
signal, signal
); );
return scoresFetcher.fetchLiveScores(player, service, serviceParams, { return scoresFetcher.fetchLiveScores(player, service, serviceParams, {

@ -21,7 +21,7 @@ export default () => {
page, page,
priority, priority,
signal, signal,
force, force
); );
case "accsaber": case "accsaber":
return await leaderboardService.fetchAccSaberPage( return await leaderboardService.fetchAccSaberPage(
@ -29,14 +29,14 @@ export default () => {
page, page,
priority, priority,
signal, signal,
force, force
); );
default: default:
return await leaderboardService.getFriendsLeaderboard( return await leaderboardService.getFriendsLeaderboard(
leaderboardId, leaderboardId,
priority, priority,
signal, signal,
force, force
); );
} }
}; };

@ -19,7 +19,7 @@ export default () => {
playerId, playerId,
service, service,
serviceParams = { sort: "recent", order: "desc", page: 1 }, serviceParams = { sort: "recent", order: "desc", page: 1 },
otherParams = {}, otherParams = {}
) => { ) => {
switch (service) { switch (service) {
case "beatsavior": case "beatsavior":
@ -36,18 +36,18 @@ export default () => {
player, player,
service, service,
serviceParams = { sort: "recent", order: "desc", page: 1 }, serviceParams = { sort: "recent", order: "desc", page: 1 },
otherParams = {}, otherParams = {}
) => { ) => {
switch (service) { switch (service) {
case "beatsavior": case "beatsavior":
return beatSaviorService.getPlayerScoresPage( return beatSaviorService.getPlayerScoresPage(
player?.playerId, player?.playerId,
serviceParams, serviceParams
); );
case "accsaber": case "accsaber":
return accSaberService.getPlayerScoresPage( return accSaberService.getPlayerScoresPage(
player?.playerId, player?.playerId,
serviceParams, serviceParams
); );
case "scoresaber": case "scoresaber":
default: default:
@ -57,7 +57,7 @@ export default () => {
otherParams?.refreshInterval, otherParams?.refreshInterval,
otherParams?.priority, otherParams?.priority,
otherParams?.signal, otherParams?.signal,
otherParams?.force, otherParams?.force
); );
} }
}; };

@ -15,7 +15,7 @@ export default () => {
set( set(
players players
.filter((p) => p && p.playerId && friends.includes(p.playerId)) .filter((p) => p && p.playerId && friends.includes(p.playerId))
.sort((a, b) => (a.name ? a.name.localeCompare(b.name) : 0)), .sort((a, b) => (a.name ? a.name.localeCompare(b.name) : 0))
); );
}); });

@ -23,11 +23,11 @@ export default () => {
const playerAddedUnsubscribe = eventBus.on( const playerAddedUnsubscribe = eventBus.on(
"player-profile-added", "player-profile-added",
refreshState, refreshState
); );
const playerRemovedUnsubscribe = eventBus.on( const playerRemovedUnsubscribe = eventBus.on(
"player-profile-removed", "player-profile-removed",
refreshState, refreshState
); );
const subscribe = (fn) => { const subscribe = (fn) => {

@ -18,7 +18,7 @@ export default async (refreshOnCreate = false) => {
const get = () => rankeds; const get = () => rankeds;
const refresh = async ( const refresh = async (
forceUpdate = false, forceUpdate = false,
priority = PRIORITY.BG_NORMAL, priority = PRIORITY.BG_NORMAL
) => { ) => {
await rankedsService.refresh(forceUpdate, priority); await rankedsService.refresh(forceUpdate, priority);
}; };
@ -32,7 +32,7 @@ export default async (refreshOnCreate = false) => {
"rankeds-changed", "rankeds-changed",
({ allRankeds }) => { ({ allRankeds }) => {
if (allRankeds && Object.keys(allRankeds).length) set(allRankeds); if (allRankeds && Object.keys(allRankeds).length) set(allRankeds);
}, }
); );
const subscribe = (fn) => { const subscribe = (fn) => {

@ -14,7 +14,7 @@ export function hoverable(node) {
pageY: event.pageY, pageY: event.pageY,
rect, rect,
}, },
}), })
); );
} }
@ -30,7 +30,7 @@ export function hoverable(node) {
pageX: event.pageX, pageX: event.pageX,
pageY: event.pageY, pageY: event.pageY,
}, },
}), })
); );
} }

@ -17,7 +17,7 @@ const createGlobalPubSub = () => {
`Create pub/sub channel for node ${nodeId} (${ `Create pub/sub channel for node ${nodeId} (${
isWorker ? "worker" : "browser" isWorker ? "worker" : "browser"
})`, })`,
"PubSub", "PubSub"
); );
bc = new BroadcastChannel("global-pub-sub", { webWorkerSupport: true }); bc = new BroadcastChannel("global-pub-sub", { webWorkerSupport: true });
@ -41,7 +41,7 @@ const createGlobalPubSub = () => {
if (!exists(eventName)) return; if (!exists(eventName)) return;
subscribers[eventName].forEach((handler) => subscribers[eventName].forEach((handler) =>
handler(value, isLocal, eventName), handler(value, isLocal, eventName)
); );
}; };
@ -49,7 +49,7 @@ const createGlobalPubSub = () => {
if (!exists(eventName)) return; if (!exists(eventName)) return;
subscribers[eventName] = subscribers[eventName].filter( subscribers[eventName] = subscribers[eventName].filter(
(h) => h !== handler, (h) => h !== handler
); );
}; };

@ -20,7 +20,7 @@ export const dateFromString = (str) => {
const matches = const matches =
str && isString(str) str && isString(str)
? str.match( ? str.match(
/^(\d{4}-\d{1,2}-\d{1,2})\s+(\d{1,2}:\d{1,2}(:\d{1,2})?)\sUTC$/, /^(\d{4}-\d{1,2}-\d{1,2})\s+(\d{1,2}:\d{1,2}(:\d{1,2})?)\sUTC$/
) )
: null; : null;
if (matches && matches.length >= 3) { if (matches && matches.length >= 3) {
@ -99,7 +99,7 @@ export const fromAccSaberDateString = (dateStr) =>
export function formatDateWithOptions( export function formatDateWithOptions(
val, val,
options = { localeMatcher: "best fit" }, options = { localeMatcher: "best fit" },
locale = getCurrentLocale(), locale = getCurrentLocale()
) { ) {
if (!isValidDate(val)) return null; if (!isValidDate(val)) return null;
@ -112,7 +112,7 @@ export function formatDate(
val, val,
dateStyle = "short", dateStyle = "short",
timeStyle = "medium", timeStyle = "medium",
locale = getCurrentLocale(), locale = getCurrentLocale()
) { ) {
return formatDateWithOptions( return formatDateWithOptions(
val, val,
@ -121,7 +121,7 @@ export function formatDate(
dateStyle, dateStyle,
timeStyle: timeStyle ?? undefined, timeStyle: timeStyle ?? undefined,
}, },
locale, locale
); );
} }
@ -158,12 +158,12 @@ export function formatDateRelative(val, roundFunc = Math.round, unit = "auto") {
else if (diffInSecs < 60 * 60 * 24 * 365) else if (diffInSecs < 60 * 60 * 24 * 365)
return rtf.format( return rtf.format(
-roundFunc(diffInSecs / (60 * 60 * 24 * 30)), -roundFunc(diffInSecs / (60 * 60 * 24 * 30)),
"month", "month"
); );
else else
return rtf.format( return rtf.format(
-roundFunc(diffInSecs / (60 * 60 * 24 * 365)), -roundFunc(diffInSecs / (60 * 60 * 24 * 365)),
"year", "year"
); );
default: default:

@ -24,7 +24,7 @@ export const throttle = (callback, wait) => {
lastFired = Date.now(); lastFired = Date.now();
} }
}, },
wait - (Date.now() - lastFired), wait - (Date.now() - lastFired)
); );
} }
}; };

@ -110,7 +110,7 @@ const getOrigin = () => {
}; };
export const exportJsonData = async ( export const exportJsonData = async (
filename = "ssr-db-" + new Date().toISOString().replace(/:/g, "_") + ".json", filename = "ssr-db-" + new Date().toISOString().replace(/:/g, "_") + ".json"
) => { ) => {
const inLineKeysRepositories = getInLineKeysRepositories(); const inLineKeysRepositories = getInLineKeysRepositories();
const outOfLineKeysRepositories = getOutOfLineKeysRepositories(); const outOfLineKeysRepositories = getOutOfLineKeysRepositories();
@ -119,7 +119,7 @@ export const exportJsonData = async (
await Promise.all( await Promise.all(
inLineKeysRepositories inLineKeysRepositories
.map((r) => r.repository) .map((r) => r.repository)
.map(async (repository) => repository().getAll()), .map(async (repository) => repository().getAll())
) )
).reduce( ).reduce(
(cum, repositoryData, idx) => { (cum, repositoryData, idx) => {
@ -133,7 +133,7 @@ export const exportJsonData = async (
version: db.version, version: db.version,
exportedOn: new Date(), exportedOn: new Date(),
stores: {}, stores: {},
}, }
); );
await Promise.all( await Promise.all(
@ -152,21 +152,21 @@ export const exportJsonData = async (
: respositoryValues[idx], : respositoryValues[idx],
}, },
]), ]),
[], []
); );
}), })
); );
return download( return download(
JSON.stringify(data), JSON.stringify(data),
filename, filename,
"application/json;charset=utf-8;", "application/json;charset=utf-8;"
); );
}; };
export const importJsonData = async (json) => { export const importJsonData = async (json) => {
const availableStores = repositories.map((item) => const availableStores = repositories.map((item) =>
item.repository().getStoreName(), item.repository().getStoreName()
); );
eventBus.publish("dl-manager-pause-cmd"); eventBus.publish("dl-manager-pause-cmd");
@ -184,7 +184,7 @@ export const importJsonData = async (json) => {
} }
const items = json.stores[storeName].map((value) => const items = json.stores[storeName].map((value) =>
castRepositoryItem(value, repositoryItem), castRepositoryItem(value, repositoryItem)
); );
const store = tx.objectStore(storeName); const store = tx.objectStore(storeName);
@ -209,7 +209,7 @@ function castObjectKeys(
value, value,
testKeys, testKeys,
castType = "date", castType = "date",
isOutOfLineRepository = false, isOutOfLineRepository = false
) { ) {
const cast = (v, type = "date") => { const cast = (v, type = "date") => {
switch (type) { switch (type) {
@ -225,7 +225,7 @@ function castObjectKeys(
const splittedKey = key.split("."); const splittedKey = key.split(".");
const mainKey = splittedKey.shift(); const mainKey = splittedKey.shift();
const keys = (!isOutOfLineRepository ? [mainKey] : []).concat( const keys = (!isOutOfLineRepository ? [mainKey] : []).concat(
isOutOfLineRepository && !splittedKey.length ? [""] : splittedKey, isOutOfLineRepository && !splittedKey.length ? [""] : splittedKey
); );
let valuePart = isOutOfLineRepository ? opt(value, "value") : value; let valuePart = isOutOfLineRepository ? opt(value, "value") : value;
@ -248,7 +248,7 @@ function castObjectKeys(
return v.map((innerV) => return v.map((innerV) =>
keys.length === 1 keys.length === 1
? cast(innerV, castType) ? cast(innerV, castType)
: process(innerV, keys.slice(1)), : process(innerV, keys.slice(1))
); );
default: default:
@ -279,7 +279,7 @@ const castRepositoryItem = (value, repositoryItem) => {
Object.entries(opt(repositoryItem, "casts", {})).forEach( Object.entries(opt(repositoryItem, "casts", {})).forEach(
([castType, keys]) => { ([castType, keys]) => {
value = castObjectKeys(value, keys, castType, isOutOfLineRepository); value = castObjectKeys(value, keys, castType, isOutOfLineRepository);
}, }
); );
return value; return value;

@ -3,14 +3,14 @@ import { getCurrentLocale } from "../stores/config";
export const substituteVars = (url, vars) => export const substituteVars = (url, vars) =>
Object.keys(vars).reduce( Object.keys(vars).reduce(
(cum, key) => cum.replace(new RegExp("\\${" + key + "}", "gi"), vars[key]), (cum, key) => cum.replace(new RegExp("\\${" + key + "}", "gi"), vars[key]),
url, url
); );
export function formatNumber( export function formatNumber(
num, num,
digits = 2, digits = 2,
addSign = false, addSign = false,
notANumber = null, notANumber = null
) { ) {
if (!Number.isFinite(num)) { if (!Number.isFinite(num)) {
return notANumber; return notANumber;

@ -43,7 +43,7 @@ export const opt = (obj, key, defaultValue = undefined) =>
.reduce( .reduce(
(o, i) => (o, i) =>
o && o[i] !== null && o[i] !== undefined ? o[i] : defaultValue, o && o[i] !== null && o[i] !== undefined ? o[i] : defaultValue,
obj, obj
); );
export const optSet = (obj, key, value, createKeys = true) => { export const optSet = (obj, key, value, createKeys = true) => {
const keys = key.split("."); const keys = key.split(".");

@ -59,7 +59,7 @@ export default {
}, },
logOnly: (types) => logOnly: (types) =>
(enabledTypes = arrayUnique( (enabledTypes = arrayUnique(
enabledTypes.concat(Array.isArray(types) ? types : [types]), enabledTypes.concat(Array.isArray(types) ? types : [types])
)), )),
logAll: () => (enabledTypes = []), logAll: () => (enabledTypes = []),
}; };

@ -5,7 +5,7 @@ export const delay = async (time, val, shouldReject = false, signal = null) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
const handle = setTimeout( const handle = setTimeout(
(_) => (shouldReject ? reject(val) : resolve(val)), (_) => (shouldReject ? reject(val) : resolve(val)),
time, time
); );
if (signal && signal.addEventListener) if (signal && signal.addEventListener)
@ -16,6 +16,6 @@ export const delay = async (time, val, shouldReject = false, signal = null) =>
reject(AbortError()); reject(AbortError());
}, },
{ once: true }, { once: true }
); );
}); });

@ -3,5 +3,5 @@ export const WEIGHT_COEFFICIENT = 0.965;
export const getTotalPpFromSortedPps = (ppArray, startIdx = 0) => export const getTotalPpFromSortedPps = (ppArray, startIdx = 0) =>
ppArray.reduce( ppArray.reduce(
(cum, pp, idx) => cum + Math.pow(WEIGHT_COEFFICIENT, idx + startIdx) * pp, (cum, pp, idx) => cum + Math.pow(WEIGHT_COEFFICIENT, idx + startIdx) * pp,
0, 0
); );

@ -52,7 +52,7 @@ export const getMaxScore = (blocks, maxScorePerBlock = 115) =>
(blocks >= 14 ? 8 * maxScorePerBlock * (blocks - 13) : 0) + (blocks >= 14 ? 8 * maxScorePerBlock * (blocks - 13) : 0) +
(blocks >= 6 ? 4 * maxScorePerBlock * (Math.min(blocks, 13) - 5) : 0) + (blocks >= 6 ? 4 * maxScorePerBlock * (Math.min(blocks, 13) - 5) : 0) +
(blocks >= 2 ? 2 * maxScorePerBlock * (Math.min(blocks, 5) - 1) : 0) + (blocks >= 2 ? 2 * maxScorePerBlock * (Math.min(blocks, 5) - 1) : 0) +
Math.min(blocks, 1) * maxScorePerBlock, Math.min(blocks, 1) * maxScorePerBlock
); );
export function getFixedLeaderboardMaxScore(leaderboardId, maxScore = null) { export function getFixedLeaderboardMaxScore(leaderboardId, maxScore = null) {
@ -64,7 +64,7 @@ export function getFixedLeaderboardMaxScore(leaderboardId, maxScore = null) {
export function getAccFromScore( export function getAccFromScore(
score, score,
maxSongScore = null, maxSongScore = null,
percentageInsteadOfAcc = false, percentageInsteadOfAcc = false
) { ) {
if (!score) return null; if (!score) return null;
@ -112,18 +112,18 @@ export function findDiffInfoWithDiffAndTypeFromBeatMaps(diffs, diffAndType) {
: diffs.find( : diffs.find(
(diff) => (diff) =>
diff.characteristic === diffAndType.type && diff.characteristic === diffAndType.type &&
diff.difficulty === capitalize(diffAndType.diff), diff.difficulty === capitalize(diffAndType.diff)
); );
} }
export function getMaxScoreFromSongCharacteristics( export function getMaxScoreFromSongCharacteristics(
songCharacteristics, songCharacteristics,
diffInfo, diffInfo,
maxScorePerBlock = 115, maxScorePerBlock = 115
) { ) {
const songDiffInfo = findDiffInfoWithDiffAndType( const songDiffInfo = findDiffInfoWithDiffAndType(
songCharacteristics, songCharacteristics,
diffInfo, diffInfo
); );
return songDiffInfo && songDiffInfo.length && songDiffInfo.notes return songDiffInfo && songDiffInfo.length && songDiffInfo.notes

@ -20,6 +20,6 @@ export const uuid = (a) => {
.replace( .replace(
// replacing // replacing
/[018]/g, // zeroes, ones, and eights with /[018]/g, // zeroes, ones, and eights with
uuid, // random hex digits uuid // random hex digits
); );
}; };

@ -31,7 +31,7 @@ const getRankedsFromDb = async (refreshCache = false) => {
const getRankeds = async (refreshCache = false) => const getRankeds = async (refreshCache = false) =>
resolvePromiseOrWaitForPending(`rankeds/${refreshCache}`, () => resolvePromiseOrWaitForPending(`rankeds/${refreshCache}`, () =>
getRankedsFromDb(), getRankedsFromDb()
); );
async function init() { async function init() {
@ -67,14 +67,14 @@ const getRankedScores = async (playerId, withStars = false) => {
.map(async (score) => { .map(async (score) => {
score = await produce( score = await produce(
await produce(score, (draft) => beatmapsEnhancer(draft, true)), await produce(score, (draft) => beatmapsEnhancer(draft, true)),
(draft) => accEnhancer(draft), (draft) => accEnhancer(draft)
); );
return { return {
...score, ...score,
stars: allRankeds[score?.leaderboardId]?.stars ?? null, stars: allRankeds[score?.leaderboardId]?.stars ?? null,
}; };
}), })
) )
).filter((s) => s.stars) ).filter((s) => s.stars)
: scores.filter((score) => score?.score?.pp); : scores.filter((score) => score?.score?.pp);
@ -92,7 +92,7 @@ const calcPlayerStats = async (playerId) => {
const stats = rankedScores const stats = rankedScores
.filter( .filter(
(score) => (score) =>
(score?.score?.score && score?.score?.maxScore) || score?.score?.acc, (score?.score?.score && score?.score?.maxScore) || score?.score?.acc
) )
.reduce( .reduce(
(cum, s) => { (cum, s) => {
@ -165,7 +165,7 @@ const calcPlayerStats = async (playerId) => {
playCount: rankedScores.length, playCount: rankedScores.length,
medianAcc: 0, medianAcc: 0,
stdDeviation: 0, stdDeviation: 0,
}, }
); );
stats.medianAcc = stats.medianAcc =
@ -178,8 +178,8 @@ const calcPlayerStats = async (playerId) => {
stats.stdDeviation = Math.sqrt( stats.stdDeviation = Math.sqrt(
rankedScores.reduce( rankedScores.reduce(
(sum, s) => sum + Math.pow(stats.avgAcc - s.score.acc, 2), (sum, s) => sum + Math.pow(stats.avgAcc - s.score.acc, 2),
0, 0
) / rankedScores.length, ) / rankedScores.length
); );
delete stats.totalAcc; delete stats.totalAcc;
@ -220,7 +220,7 @@ const calcPpBoundary = async (playerId, expectedPp = 1) => {
const ppBoundary = calcRawPpAtIdx( const ppBoundary = calcRawPpAtIdx(
rankedScorePps.slice(idx + 1), rankedScorePps.slice(idx + 1),
idx + 1, idx + 1,
expectedPp, expectedPp
); );
eventBus.publish("player-pp-boundary-calculated", { eventBus.publish("player-pp-boundary-calculated", {