feat(player): playlists, media metadata and media keys (#336)
This commit is contained in:
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
+1
-5
@@ -3,13 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
|
||||
@@ -1,18 +1,37 @@
|
||||
type playingState = {
|
||||
songTitle: string,
|
||||
artist: string,
|
||||
title: string,
|
||||
beatmapSetId: int,
|
||||
isPlaying: bool,
|
||||
volume: int,
|
||||
muted: bool,
|
||||
hasNext: bool,
|
||||
hasPrev: bool,
|
||||
};
|
||||
|
||||
type song = {
|
||||
id: int,
|
||||
artist: string,
|
||||
title: string,
|
||||
};
|
||||
|
||||
type playlistItem = {
|
||||
id: int,
|
||||
path: string,
|
||||
title: string,
|
||||
artist: string,
|
||||
};
|
||||
|
||||
type playlist = array(playlistItem);
|
||||
|
||||
type value = {
|
||||
playingState,
|
||||
playlist,
|
||||
setPlaylist: playlist => unit,
|
||||
setAudio:
|
||||
(
|
||||
~beatmapSetId: int,
|
||||
~song: song,
|
||||
~setIsPlayable: bool => unit,
|
||||
~songTitle: string,
|
||||
~audioFilePath: option(string),
|
||||
~previewOffset: option(int)
|
||||
) =>
|
||||
@@ -21,24 +40,30 @@ type value = {
|
||||
pause: unit => unit,
|
||||
togglePlayPause: unit => unit,
|
||||
setMuted: bool => unit,
|
||||
playNext: unit => unit,
|
||||
playPrevious: unit => unit,
|
||||
};
|
||||
|
||||
let initialState: playingState = {
|
||||
songTitle: "",
|
||||
artist: "",
|
||||
title: "",
|
||||
isPlaying: false,
|
||||
beatmapSetId: 0,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
hasNext: false,
|
||||
hasPrev: false,
|
||||
};
|
||||
|
||||
module Provider = {
|
||||
let value = {
|
||||
playingState: initialState,
|
||||
playlist: [||],
|
||||
setPlaylist: playlist => (),
|
||||
setAudio:
|
||||
(
|
||||
~beatmapSetId: int,
|
||||
~song: song,
|
||||
~setIsPlayable: bool => unit,
|
||||
~songTitle: string,
|
||||
~audioFilePath: option(string),
|
||||
~previewOffset: option(int),
|
||||
) =>
|
||||
@@ -47,6 +72,8 @@ module Provider = {
|
||||
pause: () => (),
|
||||
togglePlayPause: () => (),
|
||||
setMuted: (muted: bool) => (),
|
||||
playNext: () => (),
|
||||
playPrevious: () => (),
|
||||
};
|
||||
let audioPlayerContext = React.createContext(value);
|
||||
|
||||
@@ -59,96 +86,3 @@ module Provider = {
|
||||
};
|
||||
|
||||
let useAudioPlayer = () => React.useContext(Provider.audioPlayerContext);
|
||||
|
||||
let audio = Audio.make();
|
||||
[@react.component]
|
||||
[@genType]
|
||||
let make = (~children) => {
|
||||
let (playingState, setPlayingState) = React.useState(() => initialState);
|
||||
|
||||
Audio.onended(audio, _e => {
|
||||
setPlayingState(oldState => {...oldState, isPlaying: false})
|
||||
});
|
||||
|
||||
Audio.onpause(
|
||||
audio,
|
||||
_e => {
|
||||
Js.log("PAUSEEEE");
|
||||
setPlayingState(oldState => {...oldState, isPlaying: false});
|
||||
},
|
||||
);
|
||||
|
||||
Audio.onplay(audio, _e => {
|
||||
setPlayingState(oldState => {...oldState, isPlaying: true})
|
||||
});
|
||||
|
||||
Audio.oncanplay(audio, _e =>
|
||||
setPlayingState(oldState => {...oldState, isPlaying: true})
|
||||
);
|
||||
|
||||
Audio.onvolumechange(audio, e => {
|
||||
setPlayingState(oldState => {...oldState, volume: e.target.volume})
|
||||
});
|
||||
|
||||
let setPreviewAudio = (beatmapSetId: int) => {
|
||||
Audio.setSrc(audio, {j|https://b.ppy.sh/preview/$beatmapSetId.mp3|j});
|
||||
};
|
||||
|
||||
let setAudio =
|
||||
(
|
||||
~beatmapSetId,
|
||||
~setIsPlayable: bool => unit,
|
||||
~songTitle,
|
||||
~audioFilePath: option(string),
|
||||
~previewOffset: option(int),
|
||||
) => {
|
||||
Audio.onerror(
|
||||
audio,
|
||||
_e => {
|
||||
setIsPlayable(false);
|
||||
setPlayingState(oldState => {...oldState, isPlaying: false});
|
||||
},
|
||||
);
|
||||
|
||||
switch (audioFilePath, previewOffset) {
|
||||
| (None, None) => setPreviewAudio(beatmapSetId)
|
||||
| (None, Some(_)) => setPreviewAudio(beatmapSetId)
|
||||
| (Some(audioFilePath), None) => Audio.setSrc(audio, audioFilePath)
|
||||
| (Some(audioFilePath), Some(previewOffset)) =>
|
||||
Audio.setSrc(audio, audioFilePath);
|
||||
Audio.setCurrentTime(audio, previewOffset);
|
||||
};
|
||||
|
||||
Audio.play(audio);
|
||||
setPlayingState(oldState =>
|
||||
{...oldState, isPlaying: false, beatmapSetId, songTitle}
|
||||
);
|
||||
};
|
||||
|
||||
let pause = () => {
|
||||
Audio.pause(audio);
|
||||
};
|
||||
|
||||
let play = () => {
|
||||
Audio.play(audio);
|
||||
};
|
||||
|
||||
let setVolume = Audio.setVolume(audio);
|
||||
|
||||
let togglePlayPause = () => Audio.paused(audio) ? play() : pause();
|
||||
|
||||
let setMuted = muted => {
|
||||
Audio.setMuted(audio, muted);
|
||||
setPlayingState(oldState => {...oldState, muted});
|
||||
};
|
||||
|
||||
let value = {
|
||||
playingState,
|
||||
pause,
|
||||
setAudio,
|
||||
setVolume,
|
||||
togglePlayPause,
|
||||
setMuted,
|
||||
};
|
||||
<Provider value> children </Provider>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
open AudioPlayerProvider;
|
||||
|
||||
let audio = Audio.make();
|
||||
|
||||
let _play = () => {
|
||||
Audio.play(audio);
|
||||
};
|
||||
|
||||
let _updateMetadata = (song: song) => {
|
||||
MediaMetadata.make({
|
||||
title: song.title,
|
||||
artist: song.artist,
|
||||
album: "Beatconnect",
|
||||
artwork: [|MediaMetadata.makeArtwork(song.id)|],
|
||||
})
|
||||
->MediaSession.setMediaSessionMetadata;
|
||||
};
|
||||
|
||||
let _setPreviewAudio = (beatmapSetId: int) => {
|
||||
Audio.setSrc(audio, {j|https://b.ppy.sh/preview/$beatmapSetId.mp3|j});
|
||||
};
|
||||
|
||||
let _setAudioSrc = (~audioFilePath, ~previewOffset=?, ()) => {
|
||||
Audio.setSrc(audio, audioFilePath);
|
||||
switch (previewOffset) {
|
||||
| Some(offset) => Audio.setCurrentTime(audio, offset)
|
||||
| None => ()
|
||||
};
|
||||
};
|
||||
|
||||
let pause = () => {
|
||||
Audio.pause(audio);
|
||||
};
|
||||
|
||||
let togglePlayPause = () => Audio.paused(audio) ? _play() : pause();
|
||||
|
||||
let setVolume = Audio.setVolume(audio);
|
||||
|
||||
[@react.component]
|
||||
[@genType]
|
||||
let make = (~children) => {
|
||||
let (playingState, setPlayingState) = React.useState(() => initialState);
|
||||
let (playlist: playlist, setPlaylist) = React.useState(() => [||]);
|
||||
|
||||
let _canPlay = (offset: int) =>
|
||||
if (playlist->Belt_Array.length > 0) {
|
||||
let currentSongIndex =
|
||||
playlist->Belt_Array.getIndexBy(item =>
|
||||
item.id == playingState.beatmapSetId
|
||||
);
|
||||
switch (currentSongIndex) {
|
||||
| None => None
|
||||
| Some(index) =>
|
||||
index + offset < playlist->Js_array.length && index + offset > (-1)
|
||||
? Some(index + offset) : None
|
||||
};
|
||||
} else {
|
||||
None;
|
||||
};
|
||||
|
||||
let _canPlayNextSong = () => _canPlay(1);
|
||||
let _canPlayPrevSong = () => _canPlay(-1);
|
||||
|
||||
let setPlaylist = (beatmapPlaylist: playlist) => {
|
||||
setPlaylist(_ => beatmapPlaylist);
|
||||
};
|
||||
|
||||
let _stop = () => {
|
||||
pause();
|
||||
setPlaylist([||]);
|
||||
};
|
||||
|
||||
React.useEffect0(() => {
|
||||
MediaSession.setActionHandler(`play, Some(_play));
|
||||
MediaSession.setActionHandler(`pause, Some(pause));
|
||||
MediaSession.setActionHandler(`stop, Some(_stop));
|
||||
None;
|
||||
});
|
||||
|
||||
let playFromPlaylist = (playlistindex: int) => {
|
||||
let nextSong = playlist[playlistindex];
|
||||
Audio.setSrc(audio, nextSong.path);
|
||||
_updateMetadata({
|
||||
id: nextSong.id,
|
||||
title: nextSong.title,
|
||||
artist: nextSong.artist,
|
||||
});
|
||||
_play();
|
||||
setPlayingState(oldState =>
|
||||
{
|
||||
...oldState,
|
||||
isPlaying: false,
|
||||
beatmapSetId: nextSong.id,
|
||||
title: nextSong.title,
|
||||
artist: nextSong.artist,
|
||||
}
|
||||
);
|
||||
setPlaylist(playlist);
|
||||
};
|
||||
|
||||
let updateMediaHandlers = () => {
|
||||
(
|
||||
switch (_canPlayNextSong()) {
|
||||
| Some(nextSongindex) =>
|
||||
setPlayingState(prevState => {...prevState, hasNext: true});
|
||||
Some(() => playFromPlaylist(nextSongindex));
|
||||
| None =>
|
||||
setPlayingState(prevState => {...prevState, hasNext: false});
|
||||
None;
|
||||
}
|
||||
)
|
||||
|> MediaSession.setActionHandler(`nexttrack);
|
||||
(
|
||||
switch (_canPlayPrevSong()) {
|
||||
| Some(prevSongindex) =>
|
||||
setPlayingState(prevState => {...prevState, hasPrev: true});
|
||||
Some(() => playFromPlaylist(prevSongindex));
|
||||
| None =>
|
||||
setPlayingState(prevState => {...prevState, hasPrev: false});
|
||||
|
||||
None;
|
||||
}
|
||||
)
|
||||
|> MediaSession.setActionHandler(`previoustrack);
|
||||
};
|
||||
|
||||
React.useEffect2(
|
||||
() => {
|
||||
updateMediaHandlers();
|
||||
None;
|
||||
},
|
||||
(playingState.beatmapSetId, playlist),
|
||||
);
|
||||
|
||||
let playNext = () => {
|
||||
switch (_canPlayNextSong()) {
|
||||
| Some(nextSong) => playFromPlaylist(nextSong)
|
||||
| None => ()
|
||||
};
|
||||
}
|
||||
|
||||
let playPrevious = () => {
|
||||
switch (_canPlayPrevSong()) {
|
||||
| Some(prevSong) => playFromPlaylist(prevSong)
|
||||
| None => ()
|
||||
};
|
||||
}
|
||||
|
||||
let setAudio =
|
||||
(
|
||||
~song: song,
|
||||
~setIsPlayable: bool => unit,
|
||||
~audioFilePath: option(string),
|
||||
~previewOffset: option(int),
|
||||
) => {
|
||||
Audio.onerror(
|
||||
audio,
|
||||
_e => {
|
||||
setIsPlayable(false);
|
||||
setPlayingState(oldState => {...oldState, isPlaying: false});
|
||||
},
|
||||
);
|
||||
|
||||
setPlaylist([||]);
|
||||
_updateMetadata(song);
|
||||
|
||||
switch (audioFilePath, previewOffset) {
|
||||
| (None, None) => _setPreviewAudio(song.id)
|
||||
| (None, Some(_)) => _setPreviewAudio(song.id)
|
||||
| (Some(audioFilePath), None) => _setAudioSrc(~audioFilePath, ())
|
||||
| (Some(audioFilePath), Some(previewOffset)) =>
|
||||
_setAudioSrc(~audioFilePath, ~previewOffset, ())
|
||||
};
|
||||
|
||||
_play();
|
||||
setPlayingState(oldState =>
|
||||
{
|
||||
...oldState,
|
||||
isPlaying: false,
|
||||
beatmapSetId: song.id,
|
||||
artist: song.artist,
|
||||
title: song.title,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
let setMuted = muted => {
|
||||
Audio.setMuted(audio, muted);
|
||||
setPlayingState(oldState => {...oldState, muted});
|
||||
};
|
||||
|
||||
Audio.onended(audio, _e => {
|
||||
switch (_canPlayNextSong()) {
|
||||
| Some(nextSongindex) => playFromPlaylist(nextSongindex)
|
||||
| None => ()
|
||||
}
|
||||
});
|
||||
|
||||
Audio.onpause(
|
||||
audio,
|
||||
_e => {
|
||||
Js.log("PAUSEEEE");
|
||||
setPlayingState(oldState => {...oldState, isPlaying: false});
|
||||
},
|
||||
);
|
||||
|
||||
Audio.onplay(audio, _e => {
|
||||
setPlayingState(oldState => {...oldState, isPlaying: true})
|
||||
});
|
||||
|
||||
Audio.oncanplay(audio, _e =>
|
||||
setPlayingState(oldState => {...oldState, isPlaying: true})
|
||||
);
|
||||
|
||||
Audio.onvolumechange(audio, e => {
|
||||
setPlayingState(oldState => {...oldState, volume: e.target.volume})
|
||||
});
|
||||
|
||||
let value = {
|
||||
playingState,
|
||||
pause,
|
||||
setAudio,
|
||||
setPlaylist,
|
||||
setVolume,
|
||||
togglePlayPause,
|
||||
setMuted,
|
||||
playlist,
|
||||
playNext,
|
||||
playPrevious,
|
||||
};
|
||||
<Provider value> children </Provider>;
|
||||
};
|
||||
@@ -13,7 +13,7 @@ import config from '../../../../shared/config';
|
||||
import { useDownloadHistory } from '../../../Providers/HistoryProvider';
|
||||
import { getOsuSongPath } from '../../Settings/reducer/selectors';
|
||||
import { getAudioFilePath } from './item.utils';
|
||||
import { getThumbUrl } from '../../../../shared/ppy.helpers';
|
||||
import { getListCoverUrl, getThumbUrl } from '../../../../shared/PpyHelpers.bs';
|
||||
|
||||
const useStyle = createUseStyles({
|
||||
listItem: {
|
||||
@@ -41,10 +41,12 @@ const useStyle = createUseStyles({
|
||||
},
|
||||
thumbnail: {
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
width: '50px',
|
||||
height: '40px',
|
||||
margin: '5px 15px 5px 35px',
|
||||
position: 'relative',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
||||
'& .playIco': {
|
||||
position: 'absolute',
|
||||
content: '',
|
||||
@@ -114,8 +116,17 @@ const BeatmapListItem = ({ index, style, data, osuSongPath }) => {
|
||||
|
||||
const playPreview = () => {
|
||||
if (isSelected) audioPlayer.togglePlayPause();
|
||||
else if (isLibraryMode) audioPlayer.setAudio(id, () => {}, `${title} - ${artist}`, audioPath || undefined);
|
||||
else audioPlayer.setAudio(id, () => {}, `${title} - ${artist}`, audioPath || undefined, previewTime || undefined);
|
||||
else if (isLibraryMode) {
|
||||
audioPlayer.setAudio({ id, title, artist }, () => {}, audioPath || undefined);
|
||||
audioPlayer.setPlaylist(
|
||||
items.map(({ id: mapId, title: mapTitle, artist: mapArtist }) => ({
|
||||
id: mapId,
|
||||
title: mapTitle,
|
||||
artist: mapArtist,
|
||||
path: getAudioFilePath(osuSongPath, history.history[mapId].audioPath),
|
||||
})),
|
||||
);
|
||||
} else audioPlayer.setAudio({ id, title, artist }, () => {}, audioPath || undefined, previewTime || undefined);
|
||||
};
|
||||
|
||||
const downloadProgress = useCurrentDownloadItem(id);
|
||||
@@ -142,7 +153,12 @@ const BeatmapListItem = ({ index, style, data, osuSongPath }) => {
|
||||
return (
|
||||
<div style={{ ...style, top: `${parseFloat(style.top) + 50}px` }} key={id} onClick={handleClick}>
|
||||
<div className={classes.listItem} style={wrapperStyle}>
|
||||
<div className={`${classes.thumbnail} thumbnail`} style={{ backgroundImage: `url(${getThumbUrl(id)})` }}>
|
||||
<div
|
||||
className={`${classes.thumbnail} thumbnail`}
|
||||
style={{
|
||||
backgroundImage: `url(${getListCoverUrl(id)}), url(${getThumbUrl(id)})`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="playIco clickable"
|
||||
style={playIcoStyle}
|
||||
|
||||
@@ -3,13 +3,17 @@ import renderIcons from '../../../helpers/renderIcons';
|
||||
import { useAudioPlayer } from '../../../Providers/AudioPlayerProvider.bs';
|
||||
import Button from '../Button';
|
||||
|
||||
const PreviewBeatmapBtn = ({ beatmapSetId, theme, setIsPLaying, songTitle }) => {
|
||||
const PreviewBeatmapBtn = ({ beatmapSetId, theme, setIsPLaying, title, artist }) => {
|
||||
const audioPlayer = useAudioPlayer();
|
||||
const [isPlayable, setIsPlayable] = useState(true);
|
||||
const isPlaying = audioPlayer.playingState.beatmapSetId === beatmapSetId && audioPlayer.playingState.isPlaying;
|
||||
if (setIsPLaying) setIsPLaying(isPlaying);
|
||||
const playPreview = () => {
|
||||
isPlaying ? audioPlayer.pause() : audioPlayer.setAudio(beatmapSetId, setIsPlayable, songTitle);
|
||||
if (isPlaying) {
|
||||
audioPlayer.pause();
|
||||
} else {
|
||||
audioPlayer.setAudio({ id: beatmapSetId, title, artist }, setIsPlayable);
|
||||
}
|
||||
};
|
||||
return isPlayable ? (
|
||||
<Button color={theme.palette.primary.accent} onClick={playPreview}>
|
||||
|
||||
@@ -141,7 +141,8 @@ const Beatmap = ({ beatmap, noFade, autoDl, width, ...otherProps }) => {
|
||||
theme={theme}
|
||||
beatmapSetId={beatmapset_id || id}
|
||||
setIsPLaying={setIsPLaying}
|
||||
songTitle={`${title} - ${artist}`}
|
||||
title={title}
|
||||
artist={artist}
|
||||
/>
|
||||
<DownloadBeatmapBtn autoDl={autoDl} beatmapSet={beatmap} />
|
||||
<Button
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { createUseStyles } from 'react-jss';
|
||||
import { connect } from 'react-redux';
|
||||
import config from '../../../../../shared/config';
|
||||
import { getThumbUrl } from '../../../../../shared/ppy.helpers';
|
||||
import { getThumbUrl } from '../../../../../shared/PpyHelpers.bs';
|
||||
import renderIcons from '../../../../helpers/renderIcons';
|
||||
import { useAudioPlayer } from '../../../../Providers/AudioPlayerProvider.bs';
|
||||
import ScrollingText from '../../ScrollingText';
|
||||
@@ -36,6 +36,7 @@ const useStyle = createUseStyles({
|
||||
alignItems: 'center',
|
||||
},
|
||||
arrrowRight: {
|
||||
visibility: ({ hasNext }) => (hasNext ? 'visible' : 'hidden'),
|
||||
'& > svg': {
|
||||
transform: 'rotate(180deg)',
|
||||
},
|
||||
@@ -44,12 +45,14 @@ const useStyle = createUseStyles({
|
||||
cursor: 'pointer',
|
||||
},
|
||||
arrrowLeft: {
|
||||
visibility: ({ hasPrev }) => (hasPrev ? 'visible' : 'hidden'),
|
||||
width: '19px',
|
||||
height: '19px',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
songImage: {
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
borderRadius: '5px',
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
@@ -60,26 +63,34 @@ const useStyle = createUseStyles({
|
||||
});
|
||||
|
||||
const PlayingSong = ({ expended }) => {
|
||||
const classes = useStyle({ expended });
|
||||
const { playingState, togglePlayPause } = useAudioPlayer();
|
||||
const visible = playingState.songTitle;
|
||||
const { playingState, togglePlayPause, playNext, playPrevious } = useAudioPlayer();
|
||||
const classes = useStyle({ expended, hasNext: playingState.hasNext, hasPrev: playingState.hasPrev });
|
||||
const visible = playingState.beatmapSetId;
|
||||
|
||||
const handleNext = () => playNext();
|
||||
const handlePrevious = () => playPrevious();
|
||||
|
||||
if (!visible) return null;
|
||||
return (
|
||||
<div className={classes.playingSongWrapper} onClick={togglePlayPause} role="tab">
|
||||
<div className={classes.playingSongWrapper} role="tab">
|
||||
<div className={classes.expendedContentWrapper}>
|
||||
{expended && (
|
||||
<div className={classes.top}>
|
||||
<div className={classes.arrrowLeft}>{renderIcons({ name: 'Arrow' })}</div>
|
||||
<div className={classes.arrrowLeft} onClick={handlePrevious}>
|
||||
{renderIcons({ name: 'Arrow' })}
|
||||
</div>
|
||||
|
||||
<div
|
||||
onClick={togglePlayPause}
|
||||
className={classes.songImage}
|
||||
style={{ backgroundImage: `url(${getThumbUrl(playingState.beatmapSetId)})` }}
|
||||
/>
|
||||
<div className={classes.arrrowRight}>{renderIcons({ name: 'Arrow' })}</div>
|
||||
<div className={classes.arrrowRight} onClick={handleNext}>
|
||||
{renderIcons({ name: 'Arrow' })}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.bottom}>
|
||||
<div className={classes.bottom} onClick={togglePlayPause}>
|
||||
<div className={classes.icon}>
|
||||
{renderIcons({
|
||||
name: playingState.isPlaying ? 'playButton' : 'pauseButton',
|
||||
@@ -91,7 +102,7 @@ const PlayingSong = ({ expended }) => {
|
||||
</div>
|
||||
<div className={classes.label}>
|
||||
<ScrollingText
|
||||
text={playingState.songTitle}
|
||||
text={`${playingState.artist} - ${playingState.title}`}
|
||||
maxWidth={`${config.display.sidePanelExpandedLength - 44 - 2}px`}
|
||||
visible={expended}
|
||||
/>
|
||||
|
||||
@@ -31,7 +31,9 @@ let makeControlStyle = (~bgColor=?, ~spacer=false, ()) =>
|
||||
let make = (~height: int) => {
|
||||
let {AudioPlayerProvider.playingState} =
|
||||
AudioPlayerProvider.useAudioPlayer();
|
||||
let songTitle = playingState.songTitle;
|
||||
let artist = playingState.artist;
|
||||
let title = playingState.title;
|
||||
let songTitle = {j|$artist - $title|j};
|
||||
let title =
|
||||
playingState.isPlaying
|
||||
? {j|Beatconnect \u23F5 $songTitle|j} : "Beatconnect";
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import DownloadManagerProvider from './Providers/downloadManager';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
import { make as TasksProvider } from './Providers/TaskProvider.bs';
|
||||
import ThemeProvider from './Providers/ThemeProvider';
|
||||
import { make as AudioPlayerProvider } from './Providers/AudioPlayerProvider.bs';
|
||||
import { make as AudioPlayerProvider } from './Providers/Audioplayer.bs';
|
||||
import dispatchOnResize from './resize';
|
||||
|
||||
dispatchOnResize();
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
type t;
|
||||
|
||||
type artwork = {
|
||||
src: string,
|
||||
sizes: string,
|
||||
type_: string,
|
||||
};
|
||||
|
||||
type metadata = {
|
||||
title: string,
|
||||
artist: string,
|
||||
album: string,
|
||||
artwork: array(artwork),
|
||||
};
|
||||
|
||||
[@bs.new] external make: metadata => t = "MediaMetadata";
|
||||
|
||||
let makeArtwork = id => {
|
||||
src: PpyHelpers.getListCoverUrl(id),
|
||||
sizes: "160x100",
|
||||
type_: "image/jpg",
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
type t;
|
||||
type navigator;
|
||||
|
||||
[@bs.deriving {jsConverter: newType}]
|
||||
type actionType = [ | `play | `pause | `previoustrack | `nexttrack | `stop];
|
||||
|
||||
[@bs.val] external mediaSession: t = "navigator.mediaSession";
|
||||
|
||||
[@bs.val] external navigator: navigator = "navigator";
|
||||
|
||||
[@bs.send]
|
||||
external setActionHandler: (t, actionType, option(unit => unit)) => unit =
|
||||
"setActionHandler";
|
||||
|
||||
let setActionHandler = setActionHandler(mediaSession);
|
||||
|
||||
[@bs.set]
|
||||
external setMediaSessionMetadata: (t, MediaMetadata.t) => unit = "metadata";
|
||||
|
||||
let setMediaSessionMetadata = setMediaSessionMetadata(mediaSession);
|
||||
@@ -0,0 +1,3 @@
|
||||
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};
|
||||
@@ -1 +0,0 @@
|
||||
export const getThumbUrl = beatmapId => `https://b.ppy.sh/thumb/${beatmapId}.jpg`;
|
||||
Reference in New Issue
Block a user