Skip to content

Commit

Permalink
Bugfixes # 1
Browse files Browse the repository at this point in the history
- Fixed Player page navigation
- Added Back and Forward button state
- Disabled second app instance opening
- Fixed friends scires is tuck when navigating to current player page
- Removed useless API calls
  • Loading branch information
NSGolova committed Feb 10, 2022
1 parent 2b057b5 commit 880eaff
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 252 deletions.
10 changes: 10 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ const loadURL = serve({ directory: 'public' });
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
return;
}

function isDev() {
return !app.isPackaged;
}
Expand Down Expand Up @@ -78,6 +84,10 @@ function createWindow() {
mainWindow.webContents.goForward()
});

mainWindow.webContents.on('did-navigate-in-page', (event) => {
mainWindow.webContents.send('changed-navigation-options', mainWindow.webContents.canGoBack(), mainWindow.webContents.canGoForward())
});

mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
(details, callback) => {
const { requestHeaders } = details;
Expand Down
3 changes: 2 additions & 1 deletion preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ contextBridge.exposeInMainWorld(
'electron',
{
goBack: () => ipcRenderer.send('goBack'),
goForward: () => ipcRenderer.send('goForward')
goForward: () => ipcRenderer.send('goForward'),
subscribeOnpageChange: (callback) => ipcRenderer.on('changed-navigation-options', (event, canGoBack, canGoForward) => { callback(canGoBack, canGoForward) })
}
)
14 changes: 12 additions & 2 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,17 @@
setContext('pageContainer', containerStore);
let canGoBack = false;
let canGoForward = false;
function startObserving() {
window.electron.subscribeOnpageChange((newCanGoBack, newCanGoForward) => {
canGoBack = newCanGoBack;
canGoForward = newCanGoForward;
});
}
$: if (mainEl) containerStore.observe(mainEl)
$: startObserving();
</script>

<Router {url}>
Expand Down Expand Up @@ -62,8 +72,8 @@
</Router>

<div class="navigationButtons">
<Button iconFa="fas fa-arrow-left" type="primary" on:click={() => window.electron.goBack()} />
<Button iconFa="fas fa-arrow-right" type="primary" on:click={() => window.electron.goForward()} />
<Button iconFa="fas fa-arrow-left" disabled={!canGoBack} type="primary" on:click={() => window.electron.goBack()} />
<Button iconFa="fas fa-arrow-right" disabled={!canGoForward} type="primary" on:click={() => window.electron.goForward()} />
</div>

<footer>
Expand Down
3 changes: 1 addition & 2 deletions src/components/Nav.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@
</a>

{#if player}
<a href={`/u/${player.playerId}/scoresaber/recent/1`} on:click|preventDefault={() =>
navigateToPlayer(player.playerId)} transition:fade>
<a href={`/u/${player.playerId}/scoresaber/recent/1`} transition:fade>
{#if opt(player, 'playerInfo.avatar')}
<img src={player.playerInfo.avatar} class="avatar" alt="" />
{:else}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@
<div class="beat-savior" transition:fade>
<h3 class="title is-6">
<a href={`https://www.beatsavior.io/BeatSaviorSwipeCard.svelte#/profile/${playerId}`} target="_blank" rel="noreferrer">
<a href={`https://www.beatsavior.io/#/profile/${playerId}`} target="_blank" rel="noreferrer">
<span class="beatsavior-icon"></span>
<span>Beat Savior average</span>
</a>
Expand Down
14 changes: 3 additions & 11 deletions src/network/clients/scoresaber/player/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,14 @@ import createClient from '../../generic'
import {opt} from '../../../../utils/js'

const process = response => {
const {id: playerId, name, country, countryRank, badges, profilePicture, permissions, pp, rank, banned, inactive, histories: history, scoreStats} = response[0];
const {id: playerId, name, country, countryRank, badges, profilePicture, permissions, pp, rank, banned, inactive, histories: history, scoreStats} = response;

var avatar = profilePicture;
let stemProfile = response[1].response.players.length != 0;
if (stemProfile) {
avatar = response[1].response.players[0].avatarfull;
}
let externalProfileUrl = stemProfile ? response[1].response.players[0].profileurl : null;
let externalProfileCorsUrl = externalProfileUrl
let externalProfileUrl = playerId > 70000000000000000 ? "https://steamcommunity.com/profiles/" + playerId : null;

return {playerId, name, playerInfo: {
avatar,
externalProfileUrl,
externalProfileCorsUrl,
countries: [{country, rank: countryRank}],
pp,
banned,
Expand All @@ -29,9 +23,7 @@ const process = response => {
}, scoreStats: scoreStats ? scoreStats : null};
};

const get = async ({playerId, priority = queue.PRIORITY.FG_HIGH, ...queueOptions} = {}) => Promise.all([
queue.SCORESABER_API.player(playerId, priority, queueOptions),
queue.SCORESABER_API.steamProfile(playerId, priority, queueOptions)]);
const get = async ({playerId, priority = queue.PRIORITY.FG_HIGH, ...queueOptions} = {}) => queue.SCORESABER_API.player(playerId, priority, queueOptions);

const client = createClient(get, process);

Expand Down
230 changes: 0 additions & 230 deletions src/network/queues/scoresaber/page-queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export const SS_HOST = 'https://scoresaber.com';
export const SS_CDN_HOST = 'https://cdn.scoresaber.com';
const SS_CORS_HOST = 'https://scoresaber.com';
const RANKEDS_URL = SS_CORS_HOST + '/api.php?function=get-leaderboards&cat=1&limit=5000&ranked=1&page=${page}';
const PLAYER_PROFILE_URL = SS_CORS_HOST + '/u/${playerId}?page=1&sort=2'
const COUNTRY_RANKING_URL = SS_CORS_HOST + '/api/players?page=${page}&countries=${country}'
const LEADERBOARD_URL = SS_CORS_HOST + '/api/leaderboard/by-id/${leaderboardId}/info'
const LEADERBOARD_SCORES_URL = SS_CORS_HOST + '/api/leaderboard/by-id/${leaderboardId}/scores?page=${page}'
Expand Down Expand Up @@ -49,241 +48,13 @@ export default (options = {}) => {
})
}

const getImgUrl = imgUrl => {
try {
const aUrl = new URL(imgUrl);
return SS_HOST + aUrl.pathname;
}
catch(err) {
return null;
}
}

const rankeds = async (page = 1, priority = PRIORITY.BG_NORMAL, options = {}) => fetchJson(substituteVars(RANKEDS_URL, {page}), options, priority)
.then(r => {
r.body = processRankeds(r.body);

return r;
})

const processPlayerProfile = (playerId, doc) => {
cfDecryptEmail(doc);

let avatar = getImgUrl(opt(doc.querySelector('.column.avatar img'), 'src', null));

let playerName = opt(doc.querySelector('.content .column:not(.avatar) .title a'), 'innerText');
playerName = playerName ? playerName.trim() : null;

let country = getFirstRegexpMatch(/^.*?\/flags\/([^.]+)\..*$/, opt(doc.querySelector('.content .column .title img'), 'src'));
country = country ? country.toUpperCase() : null;

let pageNum = parseSsInt(opt(doc.querySelector('.pagination .pagination-list li a.is-current'), 'innerText', null));
pageNum = !isNaN(pageNum) ? pageNum : null

let pageQty = parseSsInt(opt(doc.querySelector('.pagination .pagination-list li:last-of-type'), 'innerText', null));
pageQty = !isNaN(pageQty) ? pageQty : null

let totalItems = parseSsFloat(getFirstRegexpMatch(/^\s*<strong>(?:[^:]+)\s*:?\s*<\/strong>\s*(.*)$/, opt(doc.querySelector('.columns .column:not(.is-narrow) ul li:nth-of-type(3)'), 'innerHTML')))
totalItems = !isNaN(totalItems) ? totalItems : 0;

let playerRank = parseSsInt(opt(doc.querySelector('.content .column ul li:first-of-type a:first-of-type'), 'innerText'));
playerRank = !isNaN(playerRank) ? playerRank : null;

let countryRank = parseSsInt(opt(doc.querySelector('.content .column ul li:first-of-type a[href^="/global?country="]'), 'innerText'))
countryRank = !isNaN(countryRank) ? countryRank : null;

const stats = [{key: 'Player ranking', type: 'rank', value: playerRank, countryRank: countryRank}]
.concat(
[...doc.querySelectorAll('.content .column ul li')]
.map(li => {
const matches = li.innerHTML.match(/^\s*<strong>([^:]+)\s*:?\s*<\/strong>\s*(.*)$/);
if (!matches) return null;

const mapping = [
{key: 'Performance Points', type: 'number', precision: 2, suffix: 'pp', number: true,},
{key: 'Play Count', type: 'number', precision: 0, number: true, colorVar: 'selected',},
{key: 'Total Score', type: 'number', precision: 0, number: true, colorVar: 'selected',},
{
key: 'Replays Watched by Others',
type: 'number',
precision: 0,
title: 'profile.stats.replays',
number: true,
colorVar: 'dimmed',
},
{key: 'Role', number: false, colorVar: 'dimmed'},
{key: 'Inactive Account', number: false, colorVar: 'decrease'},
{key: 'Banned', number: false, colorVar: 'decrease'},
];

const value = mapping.filter(m => m.number).map(m => m.key).includes(matches[1])
? parseSsFloat(matches[2])
: matches[2];

const item = mapping.find(m => m.key === matches[1]);
return item ? {...item, value} : {label: matches[1], value};
})
.filter(s => s)
).reduce((cum, item) => {
if (item.key)
switch (item.key) {
case 'Player ranking':
cum.rank = item.value;
cum.countryRank = item.countryRank;
break;

case 'Performance Points':
cum.pp = item.value;
break;
case 'Play Count':
cum.playCount = item.value;
break;
case 'Total Score':
cum.totalScore = item.value;
break;
case 'Replays Watched by Others':
cum.replays = item.value;
break;
case 'Role':
cum.role = item.value;
break;
case 'Inactive Account':
cum.inactiveAccount = true;
break;
case 'Banned':
cum.bannedAccount = true;
break;
}

return cum;
}, {inactiveAccount: false, bannedAccount: false});

const scores = [...doc.querySelectorAll('table.ranking tbody tr')].map(tr => {
let ret = {lastUpdated: new Date()};

const rank = tr.querySelector('th.rank');
if (rank) {
const rankMatch = parseSsInt(rank.innerText);
ret.rank = !isNaN(rankMatch) ? rankMatch : null;
} else {
ret.rank = null;
}

const song = tr.querySelector('th.song a');
if (song) {
const leaderboardId = parseInt(getFirstRegexpMatch(/leaderboard\/(\d+)/, song.href), 10);
ret.leaderboardId = leaderboardId ? leaderboardId : null;
} else {
ret.leaderboardId = null;
}

const img = tr.querySelector('th.song img');
const imgMatch = img ? img.src.match(/([^\/]+)\.(jpg|jpeg|png)$/) : null;
ret.songHash = imgMatch ? imgMatch[1] : null;

const songPp = tr.querySelector('th.song a .songTop.pp');
const songMatch = songPp
? songPp.innerHTML
.replace(/&amp;/g, '&')
.replace(/<span class="__cf_email__" data-cfemail="[^"]+">\[email&nbsp;protected]<\/span>/g, '')
.match(/^(.*?)\s*<span[^>]+>(.*?)<\/span>/)
: null;
if (songMatch) {
const songAuthorMatch = songMatch[1].match(/^(.*?)\s-\s(.*)$/);
if (songAuthorMatch) {
ret.songName = songAuthorMatch[2];
ret.songSubName = '';
ret.songAuthorName = songAuthorMatch[1];
} else {
ret.songName = songMatch[1];
ret.songSubName = '';
ret.songAuthorName = '';
}
ret.difficultyRaw = '_' + songMatch[2].replace('Expert+', 'ExpertPlus') + '_SoloStandard'
} else {
ret = Object.assign(ret, {songName: null, songSubName: null, songAuthorName: null, difficultyRaw: null});
}

const songMapper = tr.querySelector('th.song a .songTop.mapper');
ret.levelAuthorName = songMapper ? songMapper.innerText : null;

const songDate = tr.querySelector('th.song span.songBottom.time');
ret.timeSet = songDate ? dateFromString(songDate.title) : null;

const pp = parseSsFloat(opt(tr.querySelector('th.score .scoreTop.ppValue'), 'innerText'));
ret.pp = !isNaN(pp) ? pp : null;

const ppWeighted = parseSsFloat(getFirstRegexpMatch(/^\(([0-9.]+)pp\)$/, opt(tr.querySelector('th.score .scoreTop.ppWeightedValue'), 'innerText')));
ret.ppWeighted = !isNaN(ppWeighted) ? ppWeighted : null;

const scoreInfo = tr.querySelector('th.score .scoreBottom');
const scoreInfoMatch = scoreInfo ? scoreInfo.innerText.match(/^([^:]+):\s*([0-9,.]+)(?:.*?\((.*?)\))?/) : null;
if (scoreInfoMatch) {
switch (scoreInfoMatch[1]) {
case "score":
ret.acc = null;
scoreInfoMatch[3] = scoreInfoMatch[3] ? scoreInfoMatch[3].replace('-','').trim() : null
ret.mods = scoreInfoMatch[3] && scoreInfoMatch[3].length ? scoreInfoMatch[3].split(',').filter(m => m && m.trim().length) : null;
ret.score = parseSsFloat(scoreInfoMatch[2]);
break;

case "accuracy":
ret.score = null;
scoreInfoMatch[3] = scoreInfoMatch[3] ? scoreInfoMatch[3].replace('-','').trim() : null
ret.mods = scoreInfoMatch[3] && scoreInfoMatch[3].length ? scoreInfoMatch[3].split(',').filter(m => m && m.trim().length) : null;
ret.acc = parseSsFloat(scoreInfoMatch[2]);
break;
}
}

return ret;
});
const recentPlay = scores && scores.length && scores[0].timeSet ? scores[0].timeSet : null;

return {
player: {
playerInfo: {
playerId,
playerName,
avatar,
externalProfileUrl: opt(doc.querySelector('.content .column:not(.avatar) .title a'), 'href', null),
history: getFirstRegexpMatch(/data:\s*\[([0-9,]+)\]/, doc.body.innerHTML),
country,
badges: [...doc.querySelectorAll('.column.avatar center img')].map(img => ({
image: getImgUrl(img.src),
description: img.title
})),
rank: stats.rank ? stats.rank : null,
countryRank: stats.countryRank ? stats.countryRank : null,
pp: stats.pp !== undefined ? stats.pp : null,
inactive: stats.inactiveAccount ? 1 : 0,
banned: stats.bannedAccount ? 1 : 0,
role: '',
},
scoreStats: {
totalScore: stats.totalScore ? stats.totalScore : 0,
totalPlayCount: stats.playCount ? stats.playCount : 0,
replays: stats.replays ? stats.replays : null,
},
recentPlay,
recentPlayLastUpdated: recentPlay ? new Date() : null,
},
scores,
others: {
pageNum,
pageQty,
totalItems,
}
};
}

const player = async (playerId, priority = PRIORITY.FG_LOW, options = {}) => fetchHtml(substituteVars(PLAYER_PROFILE_URL, {playerId}), options, priority)
.then(r => {
r.body = processPlayerProfile(playerId, r.body);

return r
})

const processCountryRanking = (country, doc) => {

const data = doc.players
Expand Down Expand Up @@ -472,7 +243,6 @@ export default (options = {}) => {

return {
rankeds,
player,
countryRanking,
leaderboard,
...queueToReturn,
Expand Down
Loading

0 comments on commit 880eaff

Please sign in to comment.