Skip to content

Commit

Permalink
feat(App): load beatmaps thumbnails from local drive first (#449)
Browse files Browse the repository at this point in the history
  • Loading branch information
yadPe authored May 16, 2021
1 parent df60245 commit 1fdc75e
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 35 deletions.
13 changes: 6 additions & 7 deletions src/App/modules/MyLibrary/components/AllBeatmaps.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable no-continue */
import React, { useCallback, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getListCoverUrl } from '../../../../shared/PpyHelpers.bs';
import { useSelector } from 'react-redux';
import { getActiveSectionParams } from '../../../app.selectors';
import { makePlaylist, getAudioFilePath } from '../../../Providers/AudioPlayer/audioPlayer.helpers';
import { useAudioPlayer } from '../../../Providers/AudioPlayer/AudioPlayerProvider.bs';
Expand All @@ -25,18 +24,18 @@ const AllBeatmapsCollection = ({ select }) => {
return beatmaps;
};

const [artWorks, setArtWorks] = useState([]);
const [artWorksIds, setArtWorksIds] = useState([]);
const [beatmapsCount, setBeatmapsCount] = useState(0);

const getCoverArtworks = useCallback(() => {
const getCoverArtworksIds = useCallback(() => {
const beatmaps = getBeatmaps();
setBeatmapsCount(() => beatmaps.length);
const artworksLimit = Math.min(beatmaps.length >= 4 ? 4 : 1, beatmaps.length);
return beatmaps.slice(0, artworksLimit).map(beatmap => getListCoverUrl(beatmap.id));
return beatmaps.slice(0, artworksLimit).map(beatmap => beatmap.id);
}, [ready]);

useEffect(() => {
setArtWorks(() => getCoverArtworks());
setArtWorksIds(() => getCoverArtworksIds());
}, [ready]);

const isPlaying = audioPlayer.playingState.isPlaying && audioPlayer.playlistID === COLLECTION_NAME;
Expand Down Expand Up @@ -66,7 +65,7 @@ const AllBeatmapsCollection = ({ select }) => {

return (
<div className={classes.collectionWrapper} onClick={handleClick} style={{ order: -1 }}>
<CollectionCover artWorks={artWorks} onPlay={handlePlay} isPlaying={isPlaying} />
<CollectionCover artWorksIds={artWorksIds} onPlay={handlePlay} isPlaying={isPlaying} />
<p className={classes.title}>All</p>
<p className={classes.beatmapCount}>{`${beatmapsCount} songs`}</p>
</div>
Expand Down
18 changes: 8 additions & 10 deletions src/App/modules/MyLibrary/components/Collection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, { useCallback, useEffect, useState } from 'react';
import { remote } from 'electron';
import { createUseStyles } from 'react-jss';
import { useSelector } from 'react-redux';
import { getListCoverUrl } from '../../../../shared/PpyHelpers.bs';
import { makePlaylist, getAudioFilePath } from '../../../Providers/AudioPlayer/audioPlayer.helpers';
import { useAudioPlayer } from '../../../Providers/AudioPlayer/AudioPlayerProvider.bs';
import { useDownloadHistory } from '../../../Providers/HistoryProvider';
Expand Down Expand Up @@ -48,7 +47,7 @@ const Collection = ({ name, beatmapsHash, select }) => {
const osuSongPath = useSelector(getOsuSongPath);
const audioPlayer = useAudioPlayer();

const [artWorks, setArtWorks] = useState([]);
const [artWorksIds, setArtWorksIds] = useState([]);

const getBeatmapsList = useCallback(() => {
const beatmapList = [];
Expand All @@ -61,21 +60,20 @@ const Collection = ({ name, beatmapsHash, select }) => {
return beatmapList;
}, [ready, beatmapsHash.length, beatmapsHash[beatmapsHash.length - 1]]);

const getCoverArtworks = useCallback(() => {
const artWorksUrls = [];
const getCoverArtworksIds = useCallback(() => {
const artWorksIds = [];
for (let i = beatmapsHash.length - 1; i >= 0; i -= 1) {
const maybeItem = containsMD5(beatmapsHash[i]);
if (typeof maybeItem !== 'undefined') {
const url = getListCoverUrl(maybeItem.id);
if (!artWorksUrls.includes(url)) artWorksUrls.push(url);
if (!artWorksIds.includes(maybeItem.id)) artWorksIds.push(maybeItem.id);
}
if ((beatmapsHash.length < 4 && artWorksUrls.length >= 1) || artWorksUrls.length >= 4) break;
if ((beatmapsHash.length < 4 && artWorksIds.length >= 1) || artWorksIds.length >= 4) break;
}
return artWorksUrls;
return artWorksIds;
}, [ready, beatmapsHash.length, beatmapsHash[beatmapsHash.length - 1]]);

useEffect(() => {
setArtWorks(() => getCoverArtworks());
setArtWorksIds(() => getCoverArtworksIds());
}, [ready, beatmapsHash.length, beatmapsHash[beatmapsHash.length - 1]]);

const isPlaying = audioPlayer.playingState.isPlaying && audioPlayer.playlistID === name;
Expand All @@ -102,7 +100,7 @@ const Collection = ({ name, beatmapsHash, select }) => {

return (
<div className={classes.collectionWrapper} onClick={handleClick} style={{ order: beatmapsHash.length ? 0 : 1 }}>
<CollectionCover artWorks={artWorks} onPlay={handlePlay} isPlaying={isPlaying} />
<CollectionCover artWorksIds={artWorksIds} onPlay={handlePlay} isPlaying={isPlaying} />
<p className={classes.title}>{name}</p>
<p className={classes.beatmapCount}>{`${beatmapsHash.length} beatmaps`}</p>
</div>
Expand Down
12 changes: 10 additions & 2 deletions src/App/modules/MyLibrary/components/CollectionCover.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { createUseStyles } from 'react-jss';
import { useSelector } from 'react-redux';
import { resolveThumbURL } from '../../../../shared/PpyHelpers.bs';
import pauseSvg from '../../../assets/img/pause-button.svg';
import playSvg from '../../../assets/img/play-button.svg';
import { getOsuPath } from '../../Settings/reducer/selectors';

const useStyle = createUseStyles({
collectionCoverWrapper: {
Expand Down Expand Up @@ -37,7 +40,12 @@ const useStyle = createUseStyles({
},
});

const CollectionCover = ({ artWorks, onPlay, isPlaying }) => {
const CollectionCover = ({ artWorksIds = [], onPlay, isPlaying }) => {
const [artWorks, setArtWorks] = useState([]);
const osuPath = useSelector(getOsuPath);
useEffect(() => {
Promise.all(artWorksIds.map(artworkId => resolveThumbURL(artworkId, osuPath))).then(setArtWorks);
}, [artWorksIds.join('')]);
const classes = useStyle({ isPlaying });
const handlePlay = e => {
e.stopPropagation();
Expand Down
24 changes: 18 additions & 6 deletions src/App/modules/Packs/BeatmapPackDetail/Item.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { shell } from 'electron';
import React from 'react';
import React, { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { createUseStyles } from 'react-jss';
import getBeatmapInfosUrl from '../../../helpers/getBeatmapInfosUrl';
import reqImgAssets from '../../../helpers/reqImgAssets';
Expand All @@ -9,8 +10,9 @@ import { useDownloadQueue } from '../../../Providers/downloadManager';
import NewButton from '../../common/newButton';
import config from '../../../../shared/config';
import { useDownloadHistory } from '../../../Providers/HistoryProvider';
import { getListCoverUrl } from '../../../../shared/PpyHelpers.bs';
import { resolveThumbURL } from '../../../../shared/PpyHelpers.bs';
import useBeatmapSong from '../../../Providers/AudioPlayer/useBeatmapSong';
import { getOsuPath } from '../../Settings/reducer/selectors';

const useStyle = createUseStyles({
listItem: {
Expand Down Expand Up @@ -83,7 +85,7 @@ const useStyle = createUseStyles({
marginLeft: '15px',
display: 'flex',
},
ellipsis: { overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis', margin:0, },
ellipsis: { overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis', margin: 0 },
});

const BeatmapListItem = ({ index, style, data }) => {
Expand All @@ -94,6 +96,12 @@ const BeatmapListItem = ({ index, style, data }) => {
const item = items[index];
const { id, title, artist } = item;

const osuPath = useSelector(getOsuPath);
const [artworkURL, setArtWorkURL] = useState('');
useEffect(() => {
resolveThumbURL(id, osuPath).then(setArtWorkURL);
}, []);

const history = useDownloadHistory();
const { push, pauseResumeDownload, currentDownload, cancelDownload } = useDownloadQueue();
const isDownloaded = history.contains(id);
Expand Down Expand Up @@ -128,7 +136,7 @@ const BeatmapListItem = ({ index, style, data }) => {
<div
className={`${classes.thumbnail} thumbnail`}
style={{
backgroundImage: `url(${getListCoverUrl(id)})`,
backgroundImage: `url(${artworkURL})`,
}}
>
<div
Expand All @@ -141,10 +149,14 @@ const BeatmapListItem = ({ index, style, data }) => {
/>
</div>
<div className={classes.title}>
<p title={title} className={classes.ellipsis}>{title}</p>
<p title={title} className={classes.ellipsis}>
{title}
</p>
</div>
<div className={classes.artist}>
<p title={artist} className={classes.ellipsis}>{artist}</p>
<p title={artist} className={classes.ellipsis}>
{artist}
</p>
</div>
{isDownloadMode && isDownloading && (
<NewButton iconName={isPaused ? 'Download' : 'Pause'} onClick={pauseResumeDownload} borderless />
Expand Down
12 changes: 2 additions & 10 deletions src/App/modules/common/NavPanel/SidePanel/MusicPlayer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { remote } from 'electron';
import { createUseStyles } from 'react-jss';
import { connect } from 'react-redux';
import config from '../../../../../shared/config';
import { getThumbUrl } from '../../../../../shared/PpyHelpers.bs';
import { resolveThumbURL } from '../../../../../shared/PpyHelpers.bs';
import renderIcons from '../../../../helpers/renderIcons';
import { useAudioPlayer } from '../../../../Providers/AudioPlayer/AudioPlayerProvider.bs';
import ScrollingText from '../../ScrollingText';
Expand Down Expand Up @@ -76,15 +76,7 @@ const PlayingSong = ({ expended }) => {
const timeoutIdRef = useRef();

useEffect(() => {
const image = new Image();
image.onerror = () => setArtwork(DEFAULT_ARTWORK);
image.onload = () => setArtwork(getThumbUrl(playingBeatmapSetId));
image.src = getThumbUrl(playingBeatmapSetId);

return () => {
image.onerror = null;
image.onload = null;
};
resolveThumbURL(playingBeatmapSetId).then(setArtwork);
}, [playingBeatmapSetId]);

useEffect(() => {
Expand Down
20 changes: 20 additions & 0 deletions src/shared/PpyHelpers.re
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
let getThumbUrl = beatmapId => {j|https://b.ppy.sh/thumb/$beatmapId.jpg|j};

let getListCoverUrl = beatmapId => {j|https://assets.ppy.sh/beatmaps/$beatmapId/covers/list@2x.jpg|j};

let resolveThumbnail = (beatmapId, osuPath, fallbackUrl) => {
let localPath =
"file://"
++ osuPath
++ "/Data/bt/"
++ beatmapId
++ "l.jpg"
|> Js_global.encodeURI
|> Js_string.replaceByRe([%re "/\\/g/"], "/");

let thumbanil = Image.make();
thumbanil->Image.setSrc(localPath);
thumbanil->Image.decode
|> Js_promise.then_(() => Js_promise.resolve(localPath))
|> Js_promise.catch(_ => Js_promise.resolve(fallbackUrl));
};

let resolveThumbURL = (beatmapId, osuPath) =>
resolveThumbnail(beatmapId, osuPath, getListCoverUrl(beatmapId));
7 changes: 7 additions & 0 deletions src/shared/dom/Image.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type t;

[@bs.new] external make: unit => t = "Image";
[@bs.new] external makeWithSize: (~width: int, ~height: int) => t = "Image";

[@bs.send] external decode: t => Js.Promise.t(unit) = "decode";
[@bs.set] external setSrc: (t, string) => unit = "src";

0 comments on commit 1fdc75e

Please sign in to comment.