Skip to content
This repository has been archived by the owner on Jul 27, 2022. It is now read-only.

Commit

Permalink
Merge pull request #221 from CapitaineJSparrow/feature/game-banana
Browse files Browse the repository at this point in the history
Add game banana mods
  • Loading branch information
CapitaineJSparrow authored Feb 23, 2022
2 parents 3a35041 + fc50247 commit 6561c24
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 11 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "EmuSAK",
"productName": "EmuSAK",
"version": "2.0.9",
"version": "2.1.0",
"description": "This is a tool to manage your switch emulators, such as downloading saves or mods",
"repository": "https://github.com/CapitaineJSparrow/emusak-ui",
"main": ".webpack/main",
Expand Down Expand Up @@ -59,6 +59,7 @@
"@mui/material": "^5.0.4",
"@types/semver": "^7.3.9",
"adm-zip": "^0.5.9",
"cheerio": "^1.0.0-rc.10",
"cross-fetch": "^3.1.4",
"electron-is-dev": "^2.0.0",
"electron-squirrel-startup": "^1.0.0",
Expand Down
3 changes: 2 additions & 1 deletion src/main/routes/eshopData.ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ const downloadEshopData = async () => {
const updateEshopData = async () => {
try {
const eshopData = await downloadEshopData();
await fs.writeFile(eshopDataPath, JSON.stringify(eshopData), "utf-8");
await fs.writeFile(eshopDataPath(), JSON.stringify(eshopData), "utf-8");
return true;
} catch(e) {
console.log(e);
return false;
}
};
Expand Down
29 changes: 29 additions & 0 deletions src/main/routes/gamebanana.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import HttpService from "../services/HttpService";
import fetch from "node-fetch";
import cheerio from "cheerio";

export type searchProps = [string];

export const searchGameBana = async (...args: searchProps) => {
const [name] = args;
const res = await HttpService.searchGameBana(name) as unknown as { _idRow: number }[];

if (res.length === 0) {
return;
}

const url = `https://gamebanana.com/mods/games/${res[0]._idRow}?mid=SubmissionsList&vl[preset]=most_dld&vl%5Border%5D=downloads`;
const modPageContent = await fetch(url).then(r => r.text()).catch(() => "");
const $ = cheerio.load(modPageContent);
const modsIdentifiers = $("recordCell[class='Identifiers']");
const modsPreviews = $("recordCell[class='Preview']");

return modsIdentifiers.map((i, element) => {
const modElement = $(element).find("a[class='Name']");
return {
name: modElement.text().trim(),
url: modElement.attr("href"),
cover: $(modsPreviews[i]).find("img").attr("src")
};
});
};
2 changes: 2 additions & 0 deletions src/main/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { countShaders, countShadersProps, installShaders, installShadersProps, shareShaders } from "./shaders";
import { toggleCustomDnsResolver } from "./dns.ipc";
import { hasDnsFile } from "../../index";
import { searchGameBana, searchProps } from "./gamebanana";

const makeIpcRoutes = (mainWindow: BrowserWindow) => {
ipcMain.handle("load-components", async (_, ...args: loadComponentsProps) => loadComponentIpcHandler(...args));
Expand All @@ -45,6 +46,7 @@ const makeIpcRoutes = (mainWindow: BrowserWindow) => {
ipcMain.handle("share-shaders", async (_, ...args: shareShaders) => shareShaders(mainWindow, ...args));
ipcMain.handle("toggle-custom-dns", async () => toggleCustomDnsResolver());
ipcMain.handle("has-dns-file", async () => hasDnsFile);
ipcMain.handle("search-gamebanana", async (_, ...args: searchProps) => searchGameBana(...args));
};

export default makeIpcRoutes;
2 changes: 1 addition & 1 deletion src/main/routes/systemScan.ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const buildMetadataForTitleId = async (titleId: string) => {

if (eshopEntry) {
return {
title: eData[eshopEntry].name,
title: eData[eshopEntry].name.split("").filter(c => c !== "™").join(""),
img: eData[eshopEntry].iconUrl,
titleId: titleId.toUpperCase(),
};
Expand Down
10 changes: 10 additions & 0 deletions src/main/services/HttpService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ class HttpService {
})
});
}

public async searchGameBana(query: string) {
return this._fetch(
`/apiv7/Util/Game/NameMatch?_sName=${query}&_nPerpage=10&_nPage=1`,
"JSON",
"https://gamebanana.com",
{},
1
);
}
}

export default new HttpService();
8 changes: 4 additions & 4 deletions src/main/services/eshopData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ export type EshopData = {
};
};

export const eshopDataPath = process.platform === "win32"
? cacheDir
export const eshopDataPath = () => process.platform === "win32"
? path.join(cacheDir, "eshop.json")
// We can't write on executable directory on linux if it's installed in /bin (package manager) or with AppImage (readOnly)
: path.join(app.getPath("userData"), "eshop.json");

const getEshopData = async () => {
const localData = await fs.access(eshopDataPath).then(() => true).catch(() => false);
const localData = await fs.access(eshopDataPath()).then(() => true).catch(() => false);
let data: EshopData;

if (localData) {
data = JSON.parse(await fs.readFile(eshopDataPath, "utf-8"));
data = JSON.parse(await fs.readFile(eshopDataPath(), "utf-8"));
} else {
data = eshopDataBuildIn as unknown as EshopData;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ const TwoLinesTitle = styled(Typography)(() => ({
overflow: "hidden"
}));

const GameBanaCover = styled(Box)(() => ({
width: "100%",
aspectRatio: "16 / 9",
backgroundSize: "cover"
}));

const GameDetailComponent = (props: IGameDetailProps) => {
const { titleId, dataPath } = props;
const [
Expand Down Expand Up @@ -67,6 +73,7 @@ const GameDetailComponent = (props: IGameDetailProps) => {
]);
const [metaData, setMetaData]: [{ img: string, title: string, titleId: string }, Function] = useState(null);
const [compat, setCompat] = useState<GithubLabel[]>(null);
const [gameBananaMods, setGameBananaMods] = useState<{ name: string, url: string, cover: string }[]>([]);
const [localShadersCount, setLocalShadersCount] = useState(0);
const { t } = useTranslation();

Expand Down Expand Up @@ -101,6 +108,12 @@ const GameDetailComponent = (props: IGameDetailProps) => {
}
}, [titleId, needRefreshShaders]);

useEffect(() => {
metaData?.title && ipcRenderer.invoke("search-gamebanana", metaData.title).then(d => {
setGameBananaMods(Object.values(d).filter((d: any) => d.name && d.url && d.cover) as any);
});
}, [metaData]);

const renderCompatibilityData = () => (
<Grid container mb={2} sx={{ display: "flex", alignItems: "center" }}>
<Grid item xs={12}>
Expand Down Expand Up @@ -289,6 +302,34 @@ const GameDetailComponent = (props: IGameDetailProps) => {
)
}
</Grid>

{
gameBananaMods.length > 0 && (
<Box pb={3}>
<Grid container spacing={2}>
<Grid item xs={12}>
<h3>Gamebana mods (up to 20 most downloaded mods on GameBanana)</h3>
<Divider />
</Grid>

{
gameBananaMods.map(g => (
<Grid key={g.name} item xs={2}>
<a style={{ textDecoration: "none", color: "#FFF" }} href={g.url} className="no-blank-icon" target="_blank">
<TwoLinesTitle style={{ marginBottom: 6, lineHeight: 1.4 }} variant="body2" align="center">{g.name}</TwoLinesTitle>
<GameBanaCover style={{ background: `url(${g.cover}) no-repeat center center` }}>

</GameBanaCover>
</a>
</Grid>
))
}
</Grid>
</Box>
)
}


</Grid>
</>
);
Expand Down
44 changes: 40 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,30 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==

cheerio-select@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823"
integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==
dependencies:
css-select "^4.1.3"
css-what "^5.0.1"
domelementtype "^2.2.0"
domhandler "^4.2.0"
domutils "^2.7.0"

cheerio@^1.0.0-rc.10:
version "1.0.0-rc.10"
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e"
integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==
dependencies:
cheerio-select "^1.5.0"
dom-serializer "^1.3.2"
domhandler "^4.2.0"
htmlparser2 "^6.1.0"
parse5 "^6.0.1"
parse5-htmlparser2-tree-adapter "^6.0.1"
tslib "^2.2.0"

chokidar@^3.4.2, chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
Expand Down Expand Up @@ -2402,7 +2426,7 @@ css-select@^4.1.3:
domutils "^2.8.0"
nth-check "^2.0.1"

css-what@^5.1.0:
css-what@^5.0.1, css-what@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe"
integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==
Expand Down Expand Up @@ -2644,7 +2668,7 @@ dom-helpers@^5.0.1:
"@babel/runtime" "^7.8.7"
csstype "^3.0.2"

dom-serializer@^1.0.1:
dom-serializer@^1.0.1, dom-serializer@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91"
integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==
Expand All @@ -2670,7 +2694,7 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.0:
dependencies:
domelementtype "^2.2.0"

domutils@^2.5.2, domutils@^2.8.0:
domutils@^2.5.2, domutils@^2.7.0, domutils@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
Expand Down Expand Up @@ -5549,6 +5573,18 @@ parse-passwd@^1.0.0:
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=

parse5-htmlparser2-tree-adapter@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
dependencies:
parse5 "^6.0.1"

parse5@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==

parseurl@~1.3.2, parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
Expand Down Expand Up @@ -7027,7 +7063,7 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==

tslib@^2.0.3, tslib@^2.1.0:
tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
Expand Down

0 comments on commit 6561c24

Please sign in to comment.