mirror of
https://github.com/Nezumi-2711/onedrive-vercel-index.git
synced 2026-09-22 13:38:45 +00:00
render audio album thumbnails and playback controls
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
import type { OdFileObject } from '../../types'
|
import type { OdFileObject } from '../../types'
|
||||||
import { FC, useState } from 'react'
|
import { FC, useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
import ReactAudioPlayer from 'react-audio-player'
|
import ReactAudioPlayer from 'react-audio-player'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
import DownloadButtonGroup from '../DownloadBtnGtoup'
|
||||||
import { DownloadBtnContainer, PreviewContainer } from './Containers'
|
import { DownloadBtnContainer, PreviewContainer } from './Containers'
|
||||||
@@ -18,40 +19,83 @@ enum PlayerState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AudioPreview: FC<{ file: OdFileObject }> = ({ file }) => {
|
const AudioPreview: FC<{ file: OdFileObject }> = ({ file }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { asPath } = useRouter()
|
||||||
|
|
||||||
|
const rapRef = useRef<ReactAudioPlayer>(null)
|
||||||
const [playerStatus, setPlayerStatus] = useState(PlayerState.Loading)
|
const [playerStatus, setPlayerStatus] = useState(PlayerState.Loading)
|
||||||
|
|
||||||
const { t } = useTranslation()
|
// Render audio thumbnail, and also check for broken thumbnails
|
||||||
|
const thumbnail = `/api/thumbnail?path=${asPath}&size=medium`
|
||||||
|
const [brokenThumbnail, setBrokenThumbnail] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Manually get the HTML audio element and set onplaying event.
|
||||||
|
// - As the default event callbacks provided by the React component does not guarantee playing state to be set
|
||||||
|
// - properly when the user seeks through the timeline or the audio is buffered.
|
||||||
|
const rap = (rapRef.current as ReactAudioPlayer).audioEl.current
|
||||||
|
if (rap) {
|
||||||
|
rap.oncanplay = () => setPlayerStatus(PlayerState.Ready)
|
||||||
|
rap.onended = () => setPlayerStatus(PlayerState.Paused)
|
||||||
|
rap.onpause = () => setPlayerStatus(PlayerState.Paused)
|
||||||
|
rap.onplay = () => setPlayerStatus(PlayerState.Playing)
|
||||||
|
rap.onplaying = () => setPlayerStatus(PlayerState.Playing)
|
||||||
|
rap.onseeking = () => setPlayerStatus(PlayerState.Loading)
|
||||||
|
rap.onwaiting = () => setPlayerStatus(PlayerState.Loading)
|
||||||
|
rap.onerror = () => setPlayerStatus(PlayerState.Paused)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PreviewContainer>
|
<PreviewContainer>
|
||||||
<div className="flex flex-col space-y-4 md:flex-row md:space-x-4">
|
<div className="flex flex-col space-y-4 md:flex-row md:space-x-4">
|
||||||
<div className="flex aspect-square w-full items-center justify-center rounded bg-gray-100 transition-all duration-75 dark:bg-gray-700 md:w-40">
|
<div className="relative flex aspect-square w-full items-center justify-center rounded bg-gray-100 transition-all duration-75 dark:bg-gray-700 md:w-48">
|
||||||
{playerStatus === PlayerState.Loading ? (
|
<div
|
||||||
<LoadingIcon className="inline-block h-5 w-5 animate-spin" />
|
className={`absolute z-20 flex h-full w-full items-center justify-center transition-all duration-300 ${
|
||||||
|
playerStatus === PlayerState.Loading
|
||||||
|
? 'bg-white opacity-80 dark:bg-gray-800'
|
||||||
|
: 'bg-transparent opacity-0'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<LoadingIcon className="z-10 inline-block h-5 w-5 animate-spin" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!brokenThumbnail ? (
|
||||||
|
<div className="absolute m-4 rounded-full shadow-lg">
|
||||||
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
|
<img
|
||||||
|
className={`h-full w-full rounded-full object-cover object-top ${
|
||||||
|
playerStatus === PlayerState.Playing ? 'animate-spin-slow' : ''
|
||||||
|
}`}
|
||||||
|
src={thumbnail}
|
||||||
|
alt={file.name}
|
||||||
|
onError={() => setBrokenThumbnail(true)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
className={`h-5 w-5 ${playerStatus === PlayerState.Playing ? 'animate-spin' : ''}`}
|
className={`z-10 h-5 w-5 ${playerStatus === PlayerState.Playing ? 'animate-spin' : ''}`}
|
||||||
icon="music"
|
icon="music"
|
||||||
size="2x"
|
size="2x"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col space-y-2">
|
|
||||||
<div>{file.name}</div>
|
<div className="flex w-full flex-col justify-between">
|
||||||
<div className="pb-4 text-sm text-gray-500">
|
<div>
|
||||||
|
<div className="mb-2 font-medium">{file.name}</div>
|
||||||
|
<div className="mb-4 text-sm text-gray-500">
|
||||||
{t('Last modified:') + ' ' + formatModifiedDateTime(file.lastModifiedDateTime)}
|
{t('Last modified:') + ' ' + formatModifiedDateTime(file.lastModifiedDateTime)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ReactAudioPlayer
|
<ReactAudioPlayer
|
||||||
className="h-11 w-full"
|
className="h-11 w-full"
|
||||||
src={file['@microsoft.graph.downloadUrl']}
|
src={file['@microsoft.graph.downloadUrl']}
|
||||||
|
ref={rapRef}
|
||||||
controls
|
controls
|
||||||
onCanPlay={() => setPlayerStatus(PlayerState.Ready)}
|
preload="auto"
|
||||||
onPlay={() => setPlayerStatus(PlayerState.Playing)}
|
|
||||||
onPause={() => setPlayerStatus(PlayerState.Paused)}
|
|
||||||
onError={() => setPlayerStatus(PlayerState.Paused)}
|
|
||||||
onEnded={() => setPlayerStatus(PlayerState.Paused)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ module.exports = {
|
|||||||
gray: {
|
gray: {
|
||||||
850: '#222226'
|
850: '#222226'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
'spin-slow': 'spin 5s linear infinite',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user