mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-23 02:41:02 +00:00
Merge branch 'main' into feat/displaying-new-game-update
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import { wrapper } from "axios-cookiejar-support";
|
||||
import { CookieJar } from "tough-cookie";
|
||||
import { logger } from "@main/services";
|
||||
|
||||
export class DatanodesApi {
|
||||
private static readonly jar = new CookieJar();
|
||||
@@ -20,51 +21,42 @@ export class DatanodesApi {
|
||||
|
||||
await this.jar.setCookie("lang=english;", "https://datanodes.to");
|
||||
|
||||
const payload = new URLSearchParams({
|
||||
op: "download2",
|
||||
id: fileCode,
|
||||
method_free: "Free Download >>",
|
||||
dl: "1",
|
||||
});
|
||||
const formData = new FormData();
|
||||
formData.append("op", "download2");
|
||||
formData.append("id", fileCode);
|
||||
formData.append("rand", "");
|
||||
formData.append("referer", "https://datanodes.to/download");
|
||||
formData.append("method_free", "Free Download >>");
|
||||
formData.append("method_premium", "");
|
||||
formData.append("__dl", "1");
|
||||
|
||||
const response: AxiosResponse = await this.session.post(
|
||||
"https://datanodes.to/download",
|
||||
payload,
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0",
|
||||
accept: "*/*",
|
||||
"accept-language": "en-US,en;q=0.9",
|
||||
priority: "u=1, i",
|
||||
"sec-ch-ua":
|
||||
'"Google Chrome";v="141", "Not?A_Brand";v="8", "Chromium";v="141"',
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": '"Windows"',
|
||||
"sec-fetch-dest": "empty",
|
||||
"sec-fetch-mode": "cors",
|
||||
"sec-fetch-site": "same-origin",
|
||||
Referer: "https://datanodes.to/download",
|
||||
Origin: "https://datanodes.to",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
maxRedirects: 0,
|
||||
validateStatus: (status: number) => status === 302 || status < 400,
|
||||
}
|
||||
);
|
||||
|
||||
if (response.status === 302) {
|
||||
return response.headers["location"];
|
||||
}
|
||||
|
||||
if (typeof response.data === "object" && response.data.url) {
|
||||
return decodeURIComponent(response.data.url);
|
||||
}
|
||||
|
||||
const htmlContent = String(response.data);
|
||||
if (!htmlContent) {
|
||||
throw new Error("Empty response received");
|
||||
}
|
||||
|
||||
const downloadLinkRegex = /href=["'](https:\/\/[^"']+)["']/;
|
||||
const downloadLinkMatch = downloadLinkRegex.exec(htmlContent);
|
||||
if (downloadLinkMatch) {
|
||||
return downloadLinkMatch[1];
|
||||
}
|
||||
|
||||
throw new Error("Failed to get the download link");
|
||||
} catch (error) {
|
||||
console.error("Error fetching download URL:", error);
|
||||
logger.error("Error fetching download URL:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { getUserData } from "./user/get-user-data";
|
||||
import { db } from "@main/level";
|
||||
import { levelKeys } from "@main/level/sublevels";
|
||||
import type { Auth, User } from "@types";
|
||||
import { WSClient } from "./ws";
|
||||
|
||||
export interface HydraApiOptions {
|
||||
needsAuth?: boolean;
|
||||
@@ -103,8 +104,8 @@ export class HydraApi {
|
||||
await clearGamesRemoteIds();
|
||||
uploadGamesBatch();
|
||||
|
||||
// WSClient.close();
|
||||
// WSClient.connect();
|
||||
WSClient.close();
|
||||
WSClient.connect();
|
||||
|
||||
const { syncDownloadSourcesFromApi } = await import("./user");
|
||||
syncDownloadSourcesFromApi();
|
||||
|
||||
@@ -60,13 +60,20 @@ export const mergeWithRemoteGames = async () => {
|
||||
|
||||
const localGameShopAsset = await gamesShopAssetsSublevel.get(gameKey);
|
||||
|
||||
// Construct coverImageUrl if not provided by backend (Steam games use predictable pattern)
|
||||
const coverImageUrl =
|
||||
game.coverImageUrl ||
|
||||
(game.shop === "steam"
|
||||
? `https://shared.steamstatic.com/store_item_assets/steam/apps/${game.objectId}/library_600x900_2x.jpg`
|
||||
: null);
|
||||
|
||||
await gamesShopAssetsSublevel.put(gameKey, {
|
||||
updatedAt: Date.now(),
|
||||
...localGameShopAsset,
|
||||
shop: game.shop,
|
||||
objectId: game.objectId,
|
||||
title: localGame?.title || game.title, // Preserve local title if it exists
|
||||
coverImageUrl: game.coverImageUrl,
|
||||
coverImageUrl,
|
||||
libraryHeroImageUrl: game.libraryHeroImageUrl,
|
||||
libraryImageUrl: game.libraryImageUrl,
|
||||
logoImageUrl: game.logoImageUrl,
|
||||
|
||||
@@ -11,9 +11,10 @@ import { NotificationOptions, toXmlString } from "./xml";
|
||||
import { logger } from "../logger";
|
||||
import { WindowManager } from "../window-manager";
|
||||
import type { Game, UserPreferences, UserProfile } from "@types";
|
||||
import { db, levelKeys } from "@main/level";
|
||||
import { db, levelKeys, themesSublevel } from "@main/level";
|
||||
import { restartAndInstallUpdate } from "@main/events/autoupdater/restart-and-install-update";
|
||||
import { SystemPath } from "../system-path";
|
||||
import { getThemeSoundPath } from "@main/helpers";
|
||||
|
||||
async function downloadImage(url: string | null) {
|
||||
if (!url) return undefined;
|
||||
@@ -40,6 +41,27 @@ async function downloadImage(url: string | null) {
|
||||
});
|
||||
}
|
||||
|
||||
async function getAchievementSoundPath(): Promise<string> {
|
||||
try {
|
||||
const allThemes = await themesSublevel.values().all();
|
||||
const activeTheme = allThemes.find((theme) => theme.isActive);
|
||||
|
||||
if (activeTheme?.hasCustomSound) {
|
||||
const themeSoundPath = getThemeSoundPath(
|
||||
activeTheme.id,
|
||||
activeTheme.name
|
||||
);
|
||||
if (themeSoundPath) {
|
||||
return themeSoundPath;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("Failed to get theme sound path", error);
|
||||
}
|
||||
|
||||
return achievementSoundPath;
|
||||
}
|
||||
|
||||
export const publishDownloadCompleteNotification = async (game: Game) => {
|
||||
const userPreferences = await db.get<string, UserPreferences>(
|
||||
levelKeys.userPreferences,
|
||||
@@ -145,7 +167,8 @@ export const publishCombinedNewAchievementNotification = async (
|
||||
if (WindowManager.mainWindow) {
|
||||
WindowManager.mainWindow.webContents.send("on-achievement-unlocked");
|
||||
} else if (process.platform !== "linux") {
|
||||
sound.play(achievementSoundPath);
|
||||
const soundPath = await getAchievementSoundPath();
|
||||
sound.play(soundPath);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -205,6 +228,7 @@ export const publishNewAchievementNotification = async (info: {
|
||||
if (WindowManager.mainWindow) {
|
||||
WindowManager.mainWindow.webContents.send("on-achievement-unlocked");
|
||||
} else if (process.platform !== "linux") {
|
||||
sound.play(achievementSoundPath);
|
||||
const soundPath = await getAchievementSoundPath();
|
||||
sound.play(soundPath);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ export const requestSteam250 = async (path: string) => {
|
||||
if (!steamGameUrl) return null;
|
||||
|
||||
return {
|
||||
title: $title.textContent,
|
||||
title: $title.getAttribute("data-title") || "",
|
||||
objectId: steamGameUrl.split("/").pop(),
|
||||
} as Steam250Game;
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user