Skip to content

Commit

Permalink
Merge pull request #26 from OMGDuke/25/fix-blocking-moondeck-interact
Browse files Browse the repository at this point in the history
Now using an audio object instead of a dom element, no longer causes the issue that makes moondeck non interactable
  • Loading branch information
OMGDuke authored Apr 8, 2023
2 parents 8348280 + b2c9b8d commit c7ea5d1
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 79 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sdh-gamethememusic",
"version": "1.3.1",
"version": "1.3.2",
"description": "Play theme songs on your game pages",
"scripts": {
"build": "shx rm -rf dist && rollup -c",
Expand Down
31 changes: 11 additions & 20 deletions src/components/changeTheme/audioPlayer.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { DialogButton, Focusable, ServerAPI } from 'decky-frontend-lib'
import React, { useEffect, useRef, useState } from 'react'
import React, { useEffect, useState } from 'react'
import useTranslations from '../../hooks/useTranslations'
import { getAudioUrlFromVideoId } from '../../actions/audio'
import YouTubeVideo from '../../../types/YouTube'
import { FaCheck } from 'react-icons/fa'
import Spinner from '../spinner'
import useAudioPlayer from '../../hooks/useAudioPlayer'

export default function AudioPlayer({
handlePlay,
Expand All @@ -26,10 +27,11 @@ export default function AudioPlayer({
}) => void
}) {
const t = useTranslations()
const audioRef = useRef<HTMLAudioElement>(null)
const [loading, setLoading] = useState(true)
const [audioUrl, setAudio] = useState<string | undefined>()

const audioPlayer = useAudioPlayer(audioUrl)

useEffect(() => {
async function getData() {
setLoading(true)
Expand All @@ -43,22 +45,19 @@ export default function AudioPlayer({
}, [video.id])

useEffect(() => {
if (audioRef.current) {
audioRef.current.volume = volume
if (audioPlayer.isReady) {
audioPlayer.setVolume(volume)
}
}, [audioRef])
}, [audioPlayer.isReady])

useEffect(() => {
if (audioRef.current) {
video.isPlaying ? audioRef.current.play() : audioRef.current.pause()
if (audioPlayer.isReady) {
video.isPlaying ? audioPlayer.play() : audioPlayer.stop()
}
}, [video.isPlaying])

function togglePlay() {
if (audioRef?.current) {
audioRef.current.currentTime = 0
handlePlay(!video.isPlaying)
}
handlePlay(!video.isPlaying)
}

function selectAudio() {
Expand All @@ -69,6 +68,7 @@ export default function AudioPlayer({
audioUrl: audioUrl
})
}

if (!loading && !audioUrl) return <></>
return (
<div>
Expand Down Expand Up @@ -165,15 +165,6 @@ export default function AudioPlayer({
</div>
)}
</Focusable>
<audio
ref={audioRef}
src={audioUrl}
autoPlay={false}
controlsList="nodownload"
onPlay={() => audioRef.current?.play()}
onPause={() => audioRef.current?.pause()}
style={{ display: 'none' }}
></audio>
</div>
)
}
47 changes: 17 additions & 30 deletions src/components/changeTheme/gameSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,34 @@ import {
PanelSectionRow,
useParams
} from 'decky-frontend-lib'
import React, { useEffect, useRef, useState } from 'react'
import React, { useEffect, useState } from 'react'
import { getCache, updateCache } from '../../cache/musicCache'

import { getAudioUrlFromVideoId, getAudio } from '../../actions/audio'
import useTranslations from '../../hooks/useTranslations'
import { useSettings } from '../../context/settingsContext'
import { FaVolumeUp } from 'react-icons/fa'
import Spinner from '../spinner'
import useAudioPlayer from '../../hooks/useAudioPlayer'

export default function GameSettings({ serverAPI }: { serverAPI: ServerAPI }) {
const t = useTranslations()
const audioRef = useRef<HTMLAudioElement>(null)
const { state: settingsState } = useSettings()
const { appid } = useParams<{ appid: string }>()
const appDetails = appStore.GetAppOverviewByGameID(parseInt(appid))
const appName = appDetails?.display_name

const [currentAudio, setCurrentAudio] = useState<string>()
const [themeVolume, setThemeVolume] = useState(settingsState.volume)
const [isPlaying, setIsPlaying] = useState(false)
const [loading, setLoading] = useState(true)

const audioPlayer = useAudioPlayer(currentAudio)

useEffect(() => {
async function getData() {
setLoading(true)
const cache = await getCache(parseInt(appid))
if (cache?.volume) {
if (typeof cache?.volume === 'number' && isFinite(cache.volume)) {
setThemeVolume(cache.volume)
}
if (cache?.videoId?.length) {
Expand All @@ -51,24 +52,12 @@ export default function GameSettings({ serverAPI }: { serverAPI: ServerAPI }) {
getData()
}, [appid])

useEffect(() => {
if (audioRef.current) {
audioRef.current.volume = themeVolume
}
}, [audioRef, themeVolume])

function updateThemeVolume(newVol: number) {
function updateThemeVolume(newVol: number, reset?: boolean) {
setThemeVolume(newVol)
updateCache(parseInt(appid), { volume: newVol })
audioPlayer.setVolume(newVol)
updateCache(parseInt(appid), { volume: reset ? undefined : newVol })
}

useEffect(() => {
if (audioRef.current) {
audioRef.current.currentTime = 0
isPlaying ? audioRef.current.play() : audioRef.current.pause()
}
}, [audioRef, isPlaying])

return (
<div>
<h2 style={{ margin: '20px 0' }}>{appName}</h2>
Expand Down Expand Up @@ -103,28 +92,26 @@ export default function GameSettings({ serverAPI }: { serverAPI: ServerAPI }) {
</PanelSectionRow>
</div>
<DialogButton
onClick={() => setIsPlaying((v) => !v)}
onClick={audioPlayer.togglePlay}
disabled={loading}
focusable={!loading}
style={{ height: 'max-content' }}
>
{loading ? <Spinner /> : isPlaying ? t('stop') : t('play')}
{loading ? (
<Spinner />
) : audioPlayer.isPlaying ? (
t('stop')
) : (
t('play')
)}
</DialogButton>
<DialogButton
onClick={() => updateThemeVolume(settingsState.volume)}
onClick={() => updateThemeVolume(settingsState.volume, true)}
style={{ height: 'max-content' }}
>
{t('resetVolume')}
</DialogButton>
</Focusable>

<audio
ref={audioRef}
src={currentAudio}
controlsList="nodownload"
loop
style={{ display: 'none' }}
></audio>
</div>
)
}
37 changes: 10 additions & 27 deletions src/components/themePlayer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ServerAPI, useParams } from 'decky-frontend-lib'
import React, { ReactElement, useEffect, useRef, useState } from 'react'
import React, { ReactElement, useEffect } from 'react'

import useThemeMusic from '../../hooks/useThemeMusic'
import { useSettings } from '../../context/settingsContext'
import { getCache } from '../../cache/musicCache'
import useAudioPlayer from '../../hooks/useAudioPlayer'

export default function ThemePlayer({
serverAPI
Expand All @@ -12,44 +13,26 @@ export default function ThemePlayer({
}): ReactElement {
const { state: settingsState } = useSettings()
const { appid } = useParams<{ appid: string }>()
const audioRef = useRef<HTMLAudioElement>(null)
const { audio } = useThemeMusic(serverAPI, parseInt(appid))
const [volume, setVolume] = useState(settingsState.volume)
const audioPlayer = useAudioPlayer(audio.audioUrl)

useEffect(() => {
async function getData() {
const cache = await getCache(parseInt(appid))
if (typeof cache?.volume === 'number' && isFinite(cache.volume)) {
setVolume(cache.volume)
audioPlayer.setVolume(cache.volume)
} else {
audioPlayer.setVolume(settingsState.volume)
}
}
getData()
}, [])

useEffect(() => {
if (audio?.audioUrl?.length && audioRef.current) {
audioRef.current.src = audio?.audioUrl
audioRef.current.volume = volume
audioRef.current.play()
if (audio?.audioUrl?.length && audioPlayer.isReady) {
audioPlayer.play()
}
return () => {
if (audioRef.current) {
audioRef.current.pause()
audioRef.current.volume = 0
audioRef.current.src = ''
}
}
}, [audio?.audioUrl, audioRef?.current, volume])

if (!audio?.audioUrl?.length) return <></>
}, [audio?.audioUrl, audioPlayer.isReady])

return (
<audio
ref={audioRef}
className="game-theme-music-container"
style={{ display: 'none' }}
loop
controls={false}
></audio>
)
return <></>
}
80 changes: 80 additions & 0 deletions src/hooks/useAudioPlayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { useEffect, useMemo, useState } from 'react'

const useAudioPlayer = (
audioUrl: string | undefined
): {
play: () => void
pause: () => void
stop: () => void
setVolume: (volume: number) => void
togglePlay: () => void
isPlaying: boolean
isReady: boolean
} => {
const audioPlayer: HTMLAudioElement = useMemo(() => {
return new Audio()
}, [])

audioPlayer.oncanplay = () => {
setIsReady(true)
}

const [isPlaying, setIsPlaying] = useState(false)
const [isReady, setIsReady] = useState(false)

useEffect(() => {
if (audioUrl?.length) {
audioPlayer.src = audioUrl
audioPlayer.loop = true
}
}, [audioUrl])

useEffect(() => {
return () => {
unload()
}
}, [])

function play() {
audioPlayer.play()
setIsPlaying(true)
}

function pause() {
audioPlayer.pause()
setIsPlaying(false)
}

function stop() {
audioPlayer.pause()
audioPlayer.currentTime = 0
setIsPlaying(false)
}

function togglePlay() {
isPlaying ? stop() : play()
}

function setVolume(newVolume: number) {
audioPlayer.volume = newVolume
}

function unload() {
stop()
audioPlayer.src = ''
setIsPlaying(false)
setIsReady(false)
}

return {
play,
pause,
stop,
setVolume,
togglePlay,
isPlaying,
isReady
}
}

export default useAudioPlayer
3 changes: 2 additions & 1 deletion src/hooks/useThemeMusic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useEffect, useState } from 'react'

import { getAudio, getAudioUrlFromVideoId } from '../actions/audio'

import { getCache } from '../cache/musicCache'
import { getCache, updateCache } from '../cache/musicCache'
import { useSettings } from '../context/settingsContext'

const useThemeMusic = (serverAPI: ServerAPI, appId: number) => {
Expand Down Expand Up @@ -39,6 +39,7 @@ const useThemeMusic = (serverAPI: ServerAPI, appId: number) => {
if (!newAudio?.audioUrl?.length) {
return setAudio({ videoId: '', audioUrl: '' })
}
await updateCache(appId, newAudio)
return setAudio(newAudio)
}
}
Expand Down

0 comments on commit c7ea5d1

Please sign in to comment.