mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 13:56:16 +00:00
Compare commits
20 Commits
feat/disab
...
fix/librar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92d87c5d33 | ||
|
|
af884d3772 | ||
|
|
dc31ac0831 | ||
|
|
9769eecec6 | ||
|
|
91adb97013 | ||
|
|
f138b2efcb | ||
|
|
991aa05760 | ||
|
|
aff9e13bca | ||
|
|
240a75c1d0 | ||
|
|
edbe86a1fb | ||
|
|
a01e1b1709 | ||
|
|
60fd90820c | ||
|
|
798f88618e | ||
|
|
40795c34dc | ||
|
|
e335e05628 | ||
|
|
05464f25df | ||
|
|
b9830afca1 | ||
|
|
1cab73bcb4 | ||
|
|
387b3ebeac | ||
|
|
1545f42d17 |
@@ -10,6 +10,7 @@
|
||||
|
||||
[](https://github.com/hydralauncher/hydra/actions)
|
||||
[](https://github.com/hydralauncher/hydra/releases)
|
||||
[](https://community.chocolatey.org/packages/hydralauncher)
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -35,18 +35,27 @@ export class DownloadManager {
|
||||
): string | undefined {
|
||||
if (originalUrl?.includes("#")) {
|
||||
const hashPart = originalUrl.split("#")[1];
|
||||
if (hashPart && !hashPart.startsWith("http")) return hashPart;
|
||||
if (hashPart && !hashPart.startsWith("http") && hashPart.includes(".")) {
|
||||
return hashPart;
|
||||
}
|
||||
}
|
||||
|
||||
if (url.includes("#")) {
|
||||
const hashPart = url.split("#")[1];
|
||||
if (hashPart && !hashPart.startsWith("http")) return hashPart;
|
||||
if (hashPart && !hashPart.startsWith("http") && hashPart.includes(".")) {
|
||||
return hashPart;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
const filename = urlObj.pathname.split("/").pop();
|
||||
if (filename?.length) return filename;
|
||||
const pathname = urlObj.pathname;
|
||||
const pathParts = pathname.split("/");
|
||||
const filename = pathParts[pathParts.length - 1];
|
||||
|
||||
if (filename?.includes(".") && filename.length > 0) {
|
||||
return decodeURIComponent(filename);
|
||||
}
|
||||
} catch {
|
||||
// Invalid URL
|
||||
}
|
||||
@@ -55,7 +64,7 @@ export class DownloadManager {
|
||||
}
|
||||
|
||||
private static sanitizeFilename(filename: string): string {
|
||||
return filename.replace(/[<>:"/\\|?*]/g, "_");
|
||||
return filename.replaceAll(/[<>:"/\\|?*]/g, "_");
|
||||
}
|
||||
|
||||
private static createDownloadPayload(
|
||||
@@ -64,13 +73,19 @@ export class DownloadManager {
|
||||
downloadId: string,
|
||||
savePath: string
|
||||
) {
|
||||
const filename = this.extractFilename(directUrl, originalUrl);
|
||||
const filename =
|
||||
this.extractFilename(originalUrl, directUrl) ||
|
||||
this.extractFilename(directUrl);
|
||||
const sanitizedFilename = filename
|
||||
? this.sanitizeFilename(filename)
|
||||
: undefined;
|
||||
|
||||
if (sanitizedFilename) {
|
||||
logger.log(`[DownloadManager] Using filename: ${sanitizedFilename}`);
|
||||
} else {
|
||||
logger.log(
|
||||
`[DownloadManager] No filename extracted, aria2 will use default`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -227,10 +242,10 @@ export class DownloadManager {
|
||||
)
|
||||
) {
|
||||
gameFilesManager.extractDownloadedFile();
|
||||
} else {
|
||||
} else if (download.folderName) {
|
||||
gameFilesManager
|
||||
.extractFilesInDirectory(
|
||||
path.join(download.downloadPath, download.folderName!)
|
||||
path.join(download.downloadPath, download.folderName)
|
||||
)
|
||||
.then(() => gameFilesManager.setExtractionComplete());
|
||||
}
|
||||
|
||||
@@ -221,6 +221,26 @@
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
&__cover-placeholder {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 255, 255, 0.08) 0%,
|
||||
rgba(255, 255, 255, 0.04) 50%,
|
||||
rgba(255, 255, 255, 0.08) 100%
|
||||
);
|
||||
border-radius: 4px;
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { LibraryGame } from "@types";
|
||||
import { useGameCard } from "@renderer/hooks";
|
||||
import { memo } from "react";
|
||||
import { ClockIcon, AlertFillIcon, TrophyIcon } from "@primer/octicons-react";
|
||||
import { memo, useState } from "react";
|
||||
import {
|
||||
ClockIcon,
|
||||
AlertFillIcon,
|
||||
TrophyIcon,
|
||||
ImageIcon,
|
||||
} from "@primer/octicons-react";
|
||||
import "./library-game-card.scss";
|
||||
|
||||
interface LibraryGameCardProps {
|
||||
@@ -25,14 +30,9 @@ export const LibraryGameCard = memo(function LibraryGameCard({
|
||||
const { formatPlayTime, handleCardClick, handleContextMenuClick } =
|
||||
useGameCard(game, onContextMenu);
|
||||
|
||||
const coverImage = (
|
||||
game.customIconUrl ??
|
||||
game.coverImageUrl ??
|
||||
game.libraryImageUrl ??
|
||||
game.libraryHeroImageUrl ??
|
||||
game.iconUrl ??
|
||||
""
|
||||
).replaceAll("\\", "/");
|
||||
const coverImage = game.coverImageUrl?.replaceAll("\\", "/") ?? "";
|
||||
|
||||
const [imageError, setImageError] = useState(false);
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -98,12 +98,19 @@ export const LibraryGameCard = memo(function LibraryGameCard({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<img
|
||||
src={coverImage ?? undefined}
|
||||
alt={game.title}
|
||||
className="library-game-card__game-image"
|
||||
loading="lazy"
|
||||
/>
|
||||
{imageError || !coverImage ? (
|
||||
<div className="library-game-card__cover-placeholder">
|
||||
<ImageIcon size={48} />
|
||||
</div>
|
||||
) : (
|
||||
<img
|
||||
src={coverImage}
|
||||
alt={game.title}
|
||||
className="library-game-card__game-image"
|
||||
loading="lazy"
|
||||
onError={() => setImageError(true)}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
transition: all ease 0.2s;
|
||||
position: relative;
|
||||
opacity: 0.4;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.03);
|
||||
@@ -40,12 +42,17 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: globals.$background-color;
|
||||
color: #fff;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
svg {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&__badge-picture {
|
||||
|
||||
Reference in New Issue
Block a user