fix: fixed audio player erros
This commit is contained in:
+46
-45
@@ -1,12 +1,22 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useState, useRef, useEffect, MouseEvent } from 'react';
|
||||
|
||||
// Configuration
|
||||
const BASE_URL = "https://openwheels.racing/files/music/";
|
||||
|
||||
// Types
|
||||
interface Track {
|
||||
title: string;
|
||||
artist: string;
|
||||
fileName: string;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
type IconProps = { className?: string };
|
||||
|
||||
// Icons
|
||||
function PlayIcon({ className = "w-6 h-6" }) {
|
||||
function PlayIcon({ className = "w-6 h-6" }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<polygon points="5 3 19 12 5 21 5 3" />
|
||||
@@ -14,7 +24,7 @@ function PlayIcon({ className = "w-6 h-6" }) {
|
||||
);
|
||||
}
|
||||
|
||||
function PauseIcon({ className = "w-6 h-6" }) {
|
||||
function PauseIcon({ className = "w-6 h-6" }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<rect x="6" y="4" width="4" height="16" />
|
||||
@@ -23,7 +33,7 @@ function PauseIcon({ className = "w-6 h-6" }) {
|
||||
);
|
||||
}
|
||||
|
||||
function SkipNextIcon({ className = "w-6 h-6" }) {
|
||||
function SkipNextIcon({ className = "w-6 h-6" }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<polygon points="5 4 15 12 5 20 5 4" fill="currentColor" />
|
||||
@@ -32,7 +42,7 @@ function SkipNextIcon({ className = "w-6 h-6" }) {
|
||||
);
|
||||
}
|
||||
|
||||
function SkipPrevIcon({ className = "w-6 h-6" }) {
|
||||
function SkipPrevIcon({ className = "w-6 h-6" }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<polygon points="19 20 9 12 19 4 19 20" fill="currentColor" />
|
||||
@@ -41,7 +51,7 @@ function SkipPrevIcon({ className = "w-6 h-6" }) {
|
||||
);
|
||||
}
|
||||
|
||||
function VideoIcon({ className = "w-5 h-5" }) {
|
||||
function VideoIcon({ className = "w-5 h-5" }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
||||
<rect x="2" y="7" width="20" height="15" strokeLinecap="square" />
|
||||
@@ -50,7 +60,7 @@ function VideoIcon({ className = "w-5 h-5" }) {
|
||||
);
|
||||
}
|
||||
|
||||
function MusicIcon({ className = "w-5 h-5" }) {
|
||||
function MusicIcon({ className = "w-5 h-5" }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
||||
<path d="M9 18V5l12-2v13" strokeLinecap="square" />
|
||||
@@ -60,7 +70,7 @@ function MusicIcon({ className = "w-5 h-5" }) {
|
||||
);
|
||||
}
|
||||
|
||||
function RefreshIcon({ className = "w-5 h-5" }) {
|
||||
function RefreshIcon({ className = "w-5 h-5" }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<polyline points="23 4 23 10 17 10" strokeLinecap="square" />
|
||||
@@ -71,15 +81,15 @@ function RefreshIcon({ className = "w-5 h-5" }) {
|
||||
}
|
||||
|
||||
export default function MusicPage() {
|
||||
const [tracks, setTracks] = useState([]);
|
||||
const [tracks, setTracks] = useState<Track[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [currentTrackIndex, setCurrentTrackIndex] = useState(0);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [currentTime, setCurrentTime] = useState(0);
|
||||
const [duration, setDuration] = useState(0);
|
||||
const [selectedTheme, setSelectedTheme] = useState('all');
|
||||
const mediaRef = useRef(null);
|
||||
const mediaRef = useRef<HTMLMediaElement | null>(null);
|
||||
|
||||
// Fetch available tracks from the server
|
||||
const fetchTracks = async () => {
|
||||
@@ -92,15 +102,10 @@ export default function MusicPage() {
|
||||
}
|
||||
const data = await response.json();
|
||||
setTracks(data.tracks || []);
|
||||
} catch (err) {
|
||||
} catch (err: unknown) {
|
||||
console.error('Error fetching tracks:', err);
|
||||
setError(err.message);
|
||||
// Fallback to hardcoded tracks if API fails
|
||||
setTracks([
|
||||
{ title: "Alone Again", fileName: "AloneAgain.mp4", artist: "Unknown Artist", theme: "general" },
|
||||
{ title: "House Music VOL1", fileName: "deephousetherapy.mp4", artist: "Deep House Therapy", theme: "house" },
|
||||
{ title: "House Music VOL2", fileName: "videoplayback.mp4", artist: "Various Artists", theme: "house" },
|
||||
]);
|
||||
setError(err instanceof Error ? err.message : 'Unknown error');
|
||||
setTracks([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -111,8 +116,8 @@ export default function MusicPage() {
|
||||
}, []);
|
||||
|
||||
// Group tracks by theme
|
||||
const tracksByTheme = tracks.reduce((acc, track) => {
|
||||
const theme = track.theme || 'general';
|
||||
const tracksByTheme = tracks.reduce<Record<string, Track[]>>((acc, track) => {
|
||||
const theme = track?.theme || 'general';
|
||||
if (!acc[theme]) {
|
||||
acc[theme] = [];
|
||||
}
|
||||
@@ -121,10 +126,10 @@ export default function MusicPage() {
|
||||
}, {});
|
||||
|
||||
const themes = Object.keys(tracksByTheme).sort();
|
||||
|
||||
|
||||
// Get filtered tracks based on selected theme
|
||||
const filteredTracks = selectedTheme === 'all'
|
||||
? tracks
|
||||
const filteredTracks = selectedTheme === 'all'
|
||||
? tracks
|
||||
: tracksByTheme[selectedTheme] || [];
|
||||
|
||||
const currentTrack = filteredTracks[currentTrackIndex];
|
||||
@@ -140,7 +145,7 @@ export default function MusicPage() {
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [currentTrackIndex, currentTrack]);
|
||||
}, [currentTrackIndex, currentTrack]); // Removed isPlaying from deps to prevent loop
|
||||
|
||||
const togglePlayPause = () => {
|
||||
if (mediaRef.current) {
|
||||
@@ -176,7 +181,7 @@ export default function MusicPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSeek = (e) => {
|
||||
const handleSeek = (e: MouseEvent<HTMLDivElement>) => {
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const percentage = x / rect.width;
|
||||
@@ -185,14 +190,14 @@ export default function MusicPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const formatTime = (time) => {
|
||||
const formatTime = (time: number) => {
|
||||
if (isNaN(time)) return "0:00";
|
||||
const minutes = Math.floor(time / 60);
|
||||
const seconds = Math.floor(time % 60);
|
||||
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const handleThemeChange = (theme) => {
|
||||
const handleThemeChange = (theme: string) => {
|
||||
setSelectedTheme(theme);
|
||||
setCurrentTrackIndex(0);
|
||||
setIsPlaying(false);
|
||||
@@ -293,11 +298,10 @@ export default function MusicPage() {
|
||||
<span className="text-xs text-white/40 tracking-wider">FILTER:</span>
|
||||
<button
|
||||
onClick={() => handleThemeChange('all')}
|
||||
className={`px-4 py-2 border text-xs tracking-wider transition-all ${
|
||||
selectedTheme === 'all'
|
||||
className={`px-4 py-2 border text-xs tracking-wider transition-all ${selectedTheme === 'all'
|
||||
? 'border-white bg-white text-black'
|
||||
: 'border-white/20 hover:border-white/40'
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
ALL ({tracks.length})
|
||||
</button>
|
||||
@@ -305,11 +309,10 @@ export default function MusicPage() {
|
||||
<button
|
||||
key={theme}
|
||||
onClick={() => handleThemeChange(theme)}
|
||||
className={`px-4 py-2 border text-xs tracking-wider transition-all ${
|
||||
selectedTheme === theme
|
||||
className={`px-4 py-2 border text-xs tracking-wider transition-all ${selectedTheme === theme
|
||||
? 'border-white bg-white text-black'
|
||||
: 'border-white/20 hover:border-white/40'
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
{theme.toUpperCase()} ({tracksByTheme[theme].length})
|
||||
</button>
|
||||
@@ -334,7 +337,7 @@ export default function MusicPage() {
|
||||
<div className="border border-white/10 bg-black overflow-hidden">
|
||||
{isVideo ? (
|
||||
<video
|
||||
ref={mediaRef}
|
||||
ref={mediaRef as any}
|
||||
onTimeUpdate={handleTimeUpdate}
|
||||
onEnded={nextTrack}
|
||||
onPlay={() => setIsPlaying(true)}
|
||||
@@ -375,7 +378,7 @@ export default function MusicPage() {
|
||||
|
||||
{/* Audio element (hidden) */}
|
||||
<audio
|
||||
ref={mediaRef}
|
||||
ref={mediaRef as any}
|
||||
onTimeUpdate={handleTimeUpdate}
|
||||
onEnded={nextTrack}
|
||||
onPlay={() => setIsPlaying(true)}
|
||||
@@ -484,9 +487,8 @@ export default function MusicPage() {
|
||||
setCurrentTrackIndex(index);
|
||||
setIsPlaying(true);
|
||||
}}
|
||||
className={`w-full p-4 text-left transition-all hover:bg-white/5 ${
|
||||
currentTrackIndex === index ? 'bg-white/10' : ''
|
||||
}`}
|
||||
className={`w-full p-4 text-left transition-all hover:bg-white/5 ${currentTrackIndex === index ? 'bg-white/10' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<div className="flex-1 min-w-0">
|
||||
@@ -496,9 +498,8 @@ export default function MusicPage() {
|
||||
) : (
|
||||
<MusicIcon className="w-4 h-4 text-white/40 flex-shrink-0" />
|
||||
)}
|
||||
<span className={`text-sm font-semibold truncate ${
|
||||
currentTrackIndex === index ? 'text-white' : 'text-white/80'
|
||||
}`}>
|
||||
<span className={`text-sm font-semibold truncate ${currentTrackIndex === index ? 'text-white' : 'text-white/80'
|
||||
}`}>
|
||||
{track.title}
|
||||
</span>
|
||||
</div>
|
||||
@@ -561,16 +562,16 @@ export default function MusicPage() {
|
||||
{/* Animations */}
|
||||
<style jsx global>{`
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
0%, 100% {
|
||||
transform: translate(-50%, -50%) scale(0.95);
|
||||
opacity: 0.3;
|
||||
}
|
||||
50% {
|
||||
50% {
|
||||
transform: translate(-50%, -50%) scale(1.05);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes wave {
|
||||
0%, 100% { height: 4px; }
|
||||
50% { height: 16px; }
|
||||
|
||||
Reference in New Issue
Block a user