Skip to content

Commit 912bbcf

Browse files
committed
cp: image caching
1 parent e565c98 commit 912bbcf

File tree

8 files changed

+128
-6
lines changed

8 files changed

+128
-6
lines changed

.gitignore

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
/dist
14+
15+
# misc
16+
.DS_Store
17+
.env.local
18+
.env.development.local
19+
.env.test.local
20+
.env.production.local
21+
22+
# editors
23+
/.idea
24+
25+
npm-debug.log*
26+
yarn-debug.log*
27+
yarn-error.log*
28+
.eslintcache
29+
package-lock.json
30+
31+
public/**/*.js
32+
downloads*.json
33+
34+
flatpak/build
35+
.flatpak-builder
36+
.tool-versions

electron/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const userInfo = join(legendaryConfigPath, 'user.json')
5555
const installPath = join(homedir(), 'Games', 'HyperPlay')
5656
const defaultWinePrefix = join(homedir(), 'Games', 'HyperPlay', 'Prefixes')
5757
const anticheatDataPath = join(appConfigFolder, 'areweanticheatyet.json')
58+
const imagesCachePath = join(appConfigFolder, 'images-cache')
5859

5960
const { currentLogFile: currentLogFile, lastLogFile: lastLogFile } =
6061
createNewLogFileAndClearOldOnces()
@@ -190,6 +191,7 @@ export {
190191
toolsPath,
191192
defaultWinePrefix,
192193
anticheatDataPath,
194+
imagesCachePath,
193195
userHome,
194196
flatPakHome,
195197
kofiPage,

electron/images_cache.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { existsSync, createWriteStream, mkdirSync } from 'graceful-fs'
2+
import { createHash } from 'crypto'
3+
import { imagesCachePath } from './constants'
4+
import { join } from 'path'
5+
import axios from 'axios'
6+
import { protocol } from 'electron'
7+
8+
export const initImagesCache = () => {
9+
// make sure we have a folder to store the cache
10+
if (!existsSync(imagesCachePath)) {
11+
mkdirSync(imagesCachePath)
12+
}
13+
14+
// use a fake protocol for images we want to cache
15+
protocol.registerFileProtocol('imagecache', (request, callback) => {
16+
callback({ path: getImageFromCache(request.url) })
17+
})
18+
}
19+
20+
const getImageFromCache = (url: string) => {
21+
const realUrl = url.replace('imagecache://', '')
22+
// digest of the image url for the file name
23+
const digest = createHash('sha256').update(realUrl).digest('hex')
24+
const cachePath = join(imagesCachePath, digest)
25+
26+
if (!existsSync(cachePath)) {
27+
// if not found, download in the background
28+
axios
29+
.get(realUrl, { responseType: 'stream' })
30+
.then((response) => response.data.pipe(createWriteStream(cachePath)))
31+
}
32+
33+
return join(cachePath)
34+
}

electron/main.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { initImagesCache } from './images_cache'
12
import { downloadAntiCheatData } from './anticheat/utils'
23
import {
34
InstallParams,
@@ -328,6 +329,8 @@ if (!gotTheLock) {
328329
app.whenReady().then(async () => {
329330
const systemInfo = await getSystemInfo()
330331

332+
initImagesCache()
333+
331334
logInfo(
332335
['Legendary location:', join(...Object.values(getLegendaryBin()))],
333336
LogPrefix.Legendary
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React, { useState } from 'react'
2+
3+
interface CachedImageProps {
4+
src: string
5+
fallback?: string
6+
}
7+
8+
type Props = React.ImgHTMLAttributes<HTMLImageElement> & CachedImageProps
9+
10+
const CachedImage = (props: Props) => {
11+
const [useCache, setUseCache] = useState(true)
12+
const [useFallback, setUseFallback] = useState(false)
13+
14+
const onError = () => {
15+
// if not cached, tried with the real
16+
if (useCache) {
17+
setUseCache(false)
18+
} else {
19+
// if not cached and can't access real, try with the fallback
20+
if (props.fallback) {
21+
setUseFallback(true)
22+
setUseCache(true)
23+
}
24+
}
25+
}
26+
27+
let src = useFallback ? props.fallback : props.src
28+
src = useCache ? `imagecache://${src}` : src
29+
30+
return <img {...props} src={src} onError={onError} />
31+
}
32+
33+
export default CachedImage

src/components/UI/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export { default as ToggleSwitch } from './ToggleSwitch'
1010
export { default as UpdateComponent } from './UpdateComponent'
1111
export { default as SvgButton } from './SvgButton'
1212
export { default as ControllerHints } from './ControllerHints'
13+
export { default as CachedImage } from './CachedImage'

src/screens/Game/GamePicture/index.tsx

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import React from 'react'
2+
import { CachedImage } from 'src/components/UI'
3+
24
import './index.css'
35
import fallbackImage from 'src/assets/fallback-image.jpg'
6+
47
type Props = {
58
art_square: string
69
store: string
@@ -10,15 +13,25 @@ function GamePicture({ art_square, store }: Props) {
1013
function getImageFormatting() {
1114
if (art_square === 'fallback') return fallbackImage
1215
if (store === 'legendary') {
13-
return `${art_square}?h=800&resize=1&w=600`
16+
return [
17+
`${art_square}?h=800&resize=1&w=600`,
18+
`${art_square}?h=400&resize=1&w=300`
19+
]
1420
} else {
15-
return art_square
21+
return [art_square, '']
1622
}
1723
}
1824

25+
const [src, fallback] = getImageFormatting()
26+
1927
return (
2028
<div className="gamePicture">
21-
<img alt="cover-art" src={getImageFormatting()} className="gameImg" />
29+
<CachedImage
30+
alt="cover-art"
31+
className="gameImg"
32+
src={src}
33+
fallback={fallback}
34+
/>
2235
</div>
2336
)
2437
}

src/screens/Library/components/GameCard/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { useTranslation } from 'react-i18next'
1717
import ContextProvider from 'src/state/ContextProvider'
1818
import fallbackImage from 'src/assets/fallback-image.jpg'
1919
import { uninstall, updateGame } from 'src/helpers/library'
20-
import { SvgButton } from 'src/components/UI'
20+
import { CachedImage, SvgButton } from 'src/components/UI'
2121
import ContextMenu, { Item } from '../ContextMenu'
2222
import { hasProgress } from 'src/hooks/hasProgress'
2323

@@ -337,9 +337,9 @@ const GameCard = ({
337337
}
338338
>
339339
{showStoreLogos()}
340-
<img src={imageSrc} className={imgClasses} alt="cover" />
340+
<CachedImage src={imageSrc} className={imgClasses} alt="cover" />
341341
{logo && (
342-
<img
342+
<CachedImage
343343
alt="logo"
344344
src={`${logo}?h=400&resize=1&w=300`}
345345
className={logoClasses}

0 commit comments

Comments
 (0)