This commit is contained in:
gpt-engineer-app[bot]
2026-01-06 00:04:17 +00:00
parent 7cf34c1c9b
commit 0753b8ff20
6 changed files with 1197 additions and 13 deletions
+64 -13
View File
@@ -1,9 +1,12 @@
import { motion, AnimatePresence } from 'framer-motion';
import { useState, useEffect } from 'react';
import { Play, Pause, Volume2, Radio, Search, Loader2, ChevronDown, Link2, SkipBack, SkipForward, Zap } from 'lucide-react';
import { Play, Pause, Volume2, Radio, Search, Loader2, ChevronDown, Link2, SkipBack, SkipForward, Zap, Settings, Youtube, Music2 } from 'lucide-react';
import { useSettings } from '@/contexts/SettingsContext';
import { useMusic, Station } from '@/contexts/MusicContext';
import { ApiKeySettings } from '@/components/music/ApiKeySettings';
import { YouTubePlayer } from '@/components/music/YouTubePlayer';
import { SoundCloudPlayer } from '@/components/music/SoundCloudPlayer';
import { useMusicApiKeys } from '@/hooks/useMusicApiKeys';
const CATEGORIES = [
{ value: '', label: 'All Genres' },
{ value: 'pop', label: 'Pop' },
@@ -134,9 +137,11 @@ const Music = () => {
const [selectedCategory, setSelectedCategory] = useState('');
const [customUrl, setCustomUrl] = useState('');
const [customUrlError, setCustomUrlError] = useState<string | null>(null);
const [activeTab, setActiveTab] = useState<'browse' | 'presets' | 'custom'>('browse');
const [activeTab, setActiveTab] = useState<'browse' | 'presets' | 'custom' | 'youtube' | 'soundcloud'>('browse');
const [filteredStations, setFilteredStations] = useState<Station[]>([]);
const [showApiSettings, setShowApiSettings] = useState(false);
const { playSound } = useSettings();
const { hasYoutubeKey, hasSoundcloudKey } = useMusicApiKeys();
const {
isPlaying,
@@ -253,9 +258,34 @@ const Music = () => {
</h1>
<p className="font-pixel text-foreground/80">
Stream radio stations from around the world or add your own stream URL.
Stream radio stations, YouTube music, or SoundCloud mixes.
</p>
{/* API Settings Button */}
<div className="flex justify-end">
<button
onClick={() => {
playSound('click');
setShowApiSettings(true);
}}
className={`flex items-center gap-2 px-3 py-1.5 border font-pixel text-xs transition-all ${
hasYoutubeKey || hasSoundcloudKey
? 'border-primary/30 text-primary/70 hover:border-primary hover:text-primary'
: 'border-primary text-primary animate-pulse'
}`}
>
<Settings size={14} />
API Keys
{hasYoutubeKey && hasSoundcloudKey && <span className="text-primary"></span>}
</button>
</div>
<ApiKeySettings
isOpen={showApiSettings}
onClose={() => setShowApiSettings(false)}
playSound={playSound}
/>
{/* Now Playing */}
{selectedStation && (
<motion.div
@@ -339,23 +369,28 @@ const Music = () => {
)}
{/* Tabs */}
<div className="flex gap-2 border-b border-primary/30">
{(['browse', 'presets', 'custom'] as const).map((tab) => (
<div className="flex gap-1 border-b border-primary/30 overflow-x-auto pb-px">
{([
{ key: 'browse', label: 'Radio', icon: Radio },
{ key: 'youtube', label: 'YouTube', icon: Youtube },
{ key: 'soundcloud', label: 'SoundCloud', icon: Music2 },
{ key: 'presets', label: 'Electronic', icon: Zap },
{ key: 'custom', label: 'Custom', icon: Link2 },
] as const).map((tab) => (
<button
key={tab}
key={tab.key}
onClick={() => {
playSound('click');
setActiveTab(tab);
setActiveTab(tab.key);
}}
className={`px-4 py-2 font-pixel text-sm transition-all ${
activeTab === tab
className={`px-3 py-2 font-pixel text-xs transition-all flex items-center gap-1.5 whitespace-nowrap ${
activeTab === tab.key
? 'text-primary border-b-2 border-primary text-glow'
: 'text-foreground/60 hover:text-primary'
}`}
>
{tab === 'browse' && 'Browse'}
{tab === 'presets' && 'Electronic'}
{tab === 'custom' && 'Custom URL'}
<tab.icon size={14} />
{tab.label}
</button>
))}
</div>
@@ -487,6 +522,22 @@ const Music = () => {
</div>
)}
{/* YouTube Tab */}
{activeTab === 'youtube' && (
<YouTubePlayer
playSound={playSound}
onOpenSettings={() => setShowApiSettings(true)}
/>
)}
{/* SoundCloud Tab */}
{activeTab === 'soundcloud' && (
<SoundCloudPlayer
playSound={playSound}
onOpenSettings={() => setShowApiSettings(true)}
/>
)}
{/* Custom URL Tab */}
{activeTab === 'custom' && (
<div className="space-y-4">