diff --git a/main.js b/main.js index fd74b87..47a9b68 100644 --- a/main.js +++ b/main.js @@ -39,6 +39,8 @@ let installedCachedModList; let installedTotalModsCount; let onlineCachedModList; let onlineTotalModsCount; +let thunderstoreCachedModList; +let thunderstoreTotalModsCount; const bepinexFiles = [".doorstop_version", "changelog.txt", "doorstop_config.ini", "winhttp.dll"]; let bepinexVersion = bepinexStore.get("bepinex-version"); @@ -264,7 +266,11 @@ async function saveModInfo(modId, suppr = false, optionalModInfo = {}) { if (onlineCachedModList) { modInfo = onlineCachedModList.find((mod) => mod.modId == modId); } - + if (!modInfo) { + if (thunderstoreCachedModList) { + modInfo = thunderstoreCachedModList.find((mod) => mod.modId == modId); + } + } if (!modInfo) { modInfo = optionalModInfo; } @@ -514,6 +520,11 @@ ipcMain.handle("get-mods", async (event, type) => { await searchNexusMods(""); } return { onlineModsInfo: onlineCachedModList, onlineTotalCount: onlineTotalModsCount }; + } else if (type == "mods-thunderstore") { + if (!thunderstoreCachedModList) { + await searchThunderstoreMods(""); + } + return { thunderstoreModsInfo: thunderstoreCachedModList, thunderstoreTotalCount: thunderstoreTotalModsCount }; } }); @@ -646,13 +657,85 @@ async function searchNexusMods(keywords, offset = 0, count = 10, sortFilter = "d } ipcMain.handle("search-thunderstore-mods", async (event, keywords, offset, count, sortFilter, sortOrder) => { - searchThunderstoreMods(keywords, offset, count, sortFilter, sortOrder); + await searchThunderstoreMods(keywords, offset, count, sortFilter, sortOrder); }); async function searchThunderstoreMods(keywords, offset = 0, count = 10, sortFilter = "downloads", sortOrder = "DESC") { - console.log("WIP"); + const res = await fetch("https://thunderstore.io/c/hollow-knight-silksong/api/v1/package/", { + headers: { + "User-Agent": userAgent, + }, + }); + let modsInfo = await res.json(); + + const modsToRemove = [ + "f21c391c-0bc5-431d-a233-95323b95e01b", + "58c9e43a-549d-4d49-a576-4eed36775a84", + "0fc63a3b-3c69-4be5-85f8-bb127eec81b3", + "d5419c5d-c22a-4a47-b73d-ba4101f28635", + "d5b65a03-1217-4496-8af6-8dda4d763676", + "42f76853-d2a4-4520-949b-13a02fdbbbcb", + "34eac80c-5497-470e-b98c-f53421b828c0", + ]; + let reMappedModsInfo = []; + for (let i = 0; i < modsInfo.length; i++) { + modsInfo[i].source = "thunderstore"; + if (modsToRemove.includes(modsInfo[i].uuid4)) { + modsInfo.splice(i, 1); + i--; + continue; + } + reMappedModsInfo.push(reMapThunderstoreModsInfo(modsInfo[i])); + } + + const result = sortAndFilterModsList(reMappedModsInfo, keywords, offset, count, sortFilter, sortOrder); + thunderstoreCachedModList = result.list; + thunderstoreTotalModsCount = result.totalCount; } +function reMapThunderstoreModsInfo(modInfo) { + let totalDownloads = 0; + for (const version of modInfo.versions) { + totalDownloads += version.downloads; + } + + return { + author: modInfo.owner, + endorsements: modInfo.rating_score, + modId: modInfo.uuid4, + name: modInfo.name, + pictureUrl: modInfo.versions[0].icon, + summary: modInfo.versions[0].description, + updatedAt: modInfo.date_updated, + createdAt: modInfo.date_created, + version: modInfo.versions[0].version_number, + downloads: totalDownloads, + fileSize: modInfo.versions[0].file_size, + source: modInfo.source, + }; +} + +ipcMain.handle("download-thunderstore-mods", async (event, url, modId) => { + const bepinexFolderPath = path.join(loadSilksongPath(), "BepInEx"); + if (!(await fileExists(loadSilksongPath()))) { + mainWindow.webContents.send("showToast", "Path to the game invalid", "warning"); + return; + } + + if (!(await fileExists(modSavePath))) { + await fs.mkdir(modSavePath); + } + + await downloadAndUnzip(url, path.join(modSavePath, modId)); + if (await fileExists(bepinexFolderPath)) { + await fs.cp(path.join(modSavePath, modId), path.join(bepinexFolderPath, "plugins", modId), { recursive: true }); + } + + saveModInfo(modId); + mainWindow.webContents.send("showToast", "Mod downloaded successfully."); + installedCachedModList = undefined; +}); + ////////////////////////////////////////////////////// //////////////////////// MODS //////////////////////// @@ -863,6 +946,7 @@ ipcMain.handle("open-window", async (event, file) => { ipcMain.handle("launch-game", async (event, mode) => { const silksongExecutablePath = path.join(loadSilksongPath(), "Hollow Knight Silksong.exe"); + const bepinexFolderPath = path.join(loadSilksongPath(), "BepInEx"); if (!fileExists(silksongExecutablePath)) { mainWindow.webContents.send("showToast", "Path to the game invalid", "warning"); return; diff --git a/preload.js b/preload.js index 6748ba9..7153de3 100644 --- a/preload.js +++ b/preload.js @@ -65,4 +65,5 @@ contextBridge.exposeInMainWorld("mods", { contextBridge.exposeInMainWorld("thunderstore", { search: (keywords, offset, count, sortFilter, sortOrder) => ipcRenderer.invoke("search-thunderstore-mods", keywords, offset, count, sortFilter, sortOrder), + download: (url, modId) => ipcRenderer.invoke("download-thunderstore-mods", url, modId), }); diff --git a/renderer/assets/icons/plus.svg b/renderer/assets/icons/plus.svg new file mode 100644 index 0000000..32c117f --- /dev/null +++ b/renderer/assets/icons/plus.svg @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/renderer/index.html b/renderer/index.html index 53bcbdf..0869f01 100644 --- a/renderer/index.html +++ b/renderer/index.html @@ -3,7 +3,7 @@ Silk Fly Launcher - + @@ -180,6 +180,7 @@
  • by rating count
  • by date of creation
  • by date of updating
  • +
  • by size
  • diff --git a/renderer/renderer.js b/renderer/renderer.js index 44d2b78..1a75985 100644 --- a/renderer/renderer.js +++ b/renderer/renderer.js @@ -312,6 +312,73 @@ async function navigate(page) { view.appendChild(thunderstoreModsTemplateCopy); toggleSelectedListButton("sort-menu", thunderstoreSortFilter); setSortOrderButton(); + + const { thunderstoreModsInfo, thunderstoreTotalCount } = await mods.getMods(page); + thunderstoreModsTotalCount = thunderstoreTotalCount; + if (thunderstoreModsInfo == undefined) { + break; + } + for (const mod of thunderstoreModsInfo) { + if (mod.name == undefined) { + continue; + } + const modTemplateCopy = modTemplate.content.cloneNode(true); + if (mod.name) { + const modTitleText = modTemplateCopy.getElementById("mod-title"); + modTitleText.innerText = mod.name.replaceAll("_", " "); + } + if (mod.author) { + const modAuthorText = modTemplateCopy.getElementById("mod-author"); + modAuthorText.innerText = `by ${mod.author}`; + } + if (mod.endorsements) { + const modEndorsementsNumber = modTemplateCopy.getElementById("mod-endorsements-number"); + if (mod.endorsements > 1) { + modEndorsementsNumber.innerText = `${mod.endorsements} likes`; + } else { + modEndorsementsNumber.innerText = `${mod.endorsements} like`; + } + } + if (mod.downloads) { + const modDownloadsNumber = modTemplateCopy.getElementById("mod-downloads-number"); + if (mod.downloads > 1) { + modDownloadsNumber.innerText = `${mod.downloads} downloads`; + } else { + modDownloadsNumber.innerText = `${mod.downloads} download`; + } + } + if (mod.summary) { + const modDescriptionText = modTemplateCopy.getElementById("mod-description"); + modDescriptionText.innerText = mod.summary; + } + if (mod.pictureUrl) { + const modPicture = modTemplateCopy.getElementById("mod-icon"); + modPicture.src = mod.pictureUrl; + } + if (mod.version && mod.updatedAt) { + const modVersionText = modTemplateCopy.getElementById("mod-version"); + modVersionText.innerText = `V${mod.version} last updated on ${mod.updatedAt.slice(0, 10)}`; + } + + const modUrl = `https://new.thunderstore.io/c/hollow-knight-silksong/p/${mod.author}/${mod.name}`; + + const modLinkButton = modTemplateCopy.getElementById("external-link"); + modLinkButton.href = modUrl; + modLinkButton.addEventListener("click", function (event) { + event.preventDefault(); + const modLink = modLinkButton.href; + electronAPI.openExternalLink(modLink); + }); + + modDownloadButton = modTemplateCopy.getElementById("download-mod-button"); + modDownloadButton.addEventListener("click", function (event) { + event.preventDefault(); + const modDownloadLink = `https://thunderstore.io/package/download/${mod.author}/${mod.name}/${mod.version}`; + thunderstore.download(modDownloadLink, mod.modId); + }); + + thunderstoreModsContainer.appendChild(modTemplateCopy); + } break; case "general-settings": title.innerText = "Settings"; @@ -815,7 +882,7 @@ function changeModsPage(offsetChange) { thunderstoreOffset = Math.floor(thunderstoreOffset / 10) * 10; if (lastThunderstoreOffset != thunderstoreOffset) { lastThunderstoreOffset = thunderstoreOffset; - searchNexusMods(); + searchThunderstoreMods(); } } }