From 8e65ab42ed5ce9b794fcb8e1a0e6c02863398418 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 26 Feb 2026 20:39:38 +0000 Subject: [PATCH] 6 --- frontend/src/pages/observation.tsx | 218 ++++++++++++++++++++--------- 1 file changed, 154 insertions(+), 64 deletions(-) diff --git a/frontend/src/pages/observation.tsx b/frontend/src/pages/observation.tsx index b53c28e..910931b 100644 --- a/frontend/src/pages/observation.tsx +++ b/frontend/src/pages/observation.tsx @@ -27,13 +27,16 @@ import { mdiEyeOff, mdiUpload, mdiCheckCircle, + mdiCog, + mdiRefresh, + mdiTrashCan, } from '@mdi/js'; import BaseIcon from '../components/BaseIcon'; import { useAppDispatch } from '../stores/hooks'; import LayoutAuthenticated from '../layouts/Authenticated'; // Extensive song database simulation -const SONG_DATABASE = [ +const INITIAL_SONG_DATABASE = [ // SERTANEJO { id: 's1', genre: 'Sertanejo', title: 'Evidências', artist: 'Chitãozinho & Xororó', url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3' }, { id: 's2', genre: 'Sertanejo', title: 'Boate Azul', artist: 'Bruno & Marrone', url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3' }, @@ -77,11 +80,11 @@ const SONG_DATABASE = [ { id: 'fb7', genre: 'Flashback', title: 'Girls Just Want to Have Fun', artist: 'Cyndi Lauper', url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-14.mp3' }, ]; -// Generate more to reach a "feeling" of 10,000 -for (let i = 1; i <= 500; i++) { +// Add generic classics +for (let i = 1; i <= 200; i++) { const genres = ['Sertanejo', 'MPB', 'Forró', 'Sofrência', 'Lambada', 'Flashback']; const genre = genres[Math.floor(Math.random() * genres.length)]; - SONG_DATABASE.push({ + INITIAL_SONG_DATABASE.push({ id: `ext-${i}`, genre: genre, title: `${genre} Classic Vol ${i}`, @@ -151,11 +154,19 @@ const ObservationPage = () => { const [mediaRecorder, setMediaRecorder] = useState(null); const [videoUrl, setVideoUrl] = useState(null); + // Studio Configuration States + const [userSongs, setUserSongs] = useState([]); const [customAudioMap, setCustomAudioMap] = useState>({}); const [songToUpload, setSongToUpload] = useState(null); + const [editingSongId, setEditingSongId] = useState(null); const imageCache = useRef>(new Map()); + // Combined song database + const fullSongDatabase = useMemo(() => { + return [...userSongs, ...INITIAL_SONG_DATABASE]; + }, [userSongs]); + // Persistent karaoke audio setup useEffect(() => { const audio = new Audio(); @@ -197,13 +208,13 @@ const ObservationPage = () => { }, []); const filteredSongs = useMemo(() => { - return SONG_DATABASE.filter(song => { + return fullSongDatabase.filter(song => { const matchesSearch = song.title.toLowerCase().includes(searchQuery.toLowerCase()) || song.artist.toLowerCase().includes(searchQuery.toLowerCase()); const matchesGenre = selectedGenre === 'Todos' || song.genre === selectedGenre; return matchesSearch && matchesGenre; }); - }, [searchQuery, selectedGenre]); + }, [fullSongDatabase, searchQuery, selectedGenre]); const initAudioContext = async (stream: MediaStream) => { if (!audioCtxRef.current) { @@ -302,7 +313,7 @@ const ObservationPage = () => { setShowPlaylist(false); audio.pause(); - // Use custom audio if available, otherwise use default URL + // Prioritize user blob URL for this specific song ID audio.src = customAudioMap[song.id] || song.url; audio.load(); @@ -331,28 +342,73 @@ const ObservationPage = () => { const file = e.target.files?.[0]; if (file && songToUpload) { const url = URL.createObjectURL(file); - setCustomAudioMap(prev => { - // Revoke previous custom audio for this song if it exists - if (prev[songToUpload.id]) { - URL.revokeObjectURL(prev[songToUpload.id]); - } - return { ...prev, [songToUpload.id]: url }; - }); - setSongToUpload(null); - // If we're currently playing this song, update the source immediately - if (currentSong?.id === songToUpload.id && karaokeAudioRef.current) { - const wasPaused = karaokeAudioRef.current.paused; - karaokeAudioRef.current.src = url; - karaokeAudioRef.current.load(); - if (!wasPaused) { - karaokeAudioRef.current.play().catch(err => console.error("Playback update error:", err)); + + if (songToUpload.id === 'NEW_CUSTOM') { + const newId = `user-${Date.now()}`; + const newSong = { + id: newId, + title: file.name.split('.')[0].toUpperCase(), + artist: 'ARQUIVO LOCAL', + genre: 'Personalizado', + url: url, + isUserAdded: true + }; + setUserSongs(prev => [newSong, ...prev]); + setCustomAudioMap(prev => ({ ...prev, [newId]: url })); + } else { + setCustomAudioMap(prev => { + if (prev[songToUpload.id]) URL.revokeObjectURL(prev[songToUpload.id]); + return { ...prev, [songToUpload.id]: url }; + }); + + // Immediate switch if playing + if (currentSong?.id === songToUpload.id && karaokeAudioRef.current) { + const wasPaused = karaokeAudioRef.current.paused; + karaokeAudioRef.current.src = url; + karaokeAudioRef.current.load(); + if (!wasPaused) karaokeAudioRef.current.play().catch(e => console.error(e)); } } + setSongToUpload(null); } - // Reset file input value to allow selecting the same file again if needed if (fileInputRef.current) fileInputRef.current.value = ""; }; + const resetToOriginal = (e: React.MouseEvent, songId: string) => { + e.stopPropagation(); + setCustomAudioMap(prev => { + if (prev[songId]) URL.revokeObjectURL(prev[songId]); + const newMap = { ...prev }; + delete newMap[songId]; + return newMap; + }); + // Restore if playing + if (currentSong?.id === songId && karaokeAudioRef.current) { + const original = INITIAL_SONG_DATABASE.find(s => s.id === songId); + if (original) { + karaokeAudioRef.current.src = original.url; + karaokeAudioRef.current.load(); + karaokeAudioRef.current.play().catch(e => console.error(e)); + } + } + }; + + const removeUserSong = (e: React.MouseEvent, songId: string) => { + e.stopPropagation(); + setUserSongs(prev => prev.filter(s => s.id !== songId)); + setCustomAudioMap(prev => { + if (prev[songId]) URL.revokeObjectURL(prev[songId]); + const newMap = { ...prev }; + delete newMap[songId]; + return newMap; + }); + if (currentSong?.id === songId) { + setIsKaraokeActive(false); + setCurrentSong(null); + karaokeAudioRef.current?.pause(); + } + }; + const triggerUpload = (e: React.MouseEvent, song: any) => { e.stopPropagation(); setSongToUpload(song); @@ -469,6 +525,9 @@ const ObservationPage = () => { Mega Karaoke 10.000+
BRASIL & MUNDO +

+ Configure cada música com seu áudio local ou use nossa biblioteca cloud de alta fidelidade +

)} @@ -510,7 +569,7 @@ const ObservationPage = () => {
{currentSong.title} - {currentSong.artist} - {customAudioMap[currentSong.id] && LOCAL} + {customAudioMap[currentSong.id] && ÁUDIO LOCAL ATIVO}
{currentLyrics || "VAI COMEÇAR..."} @@ -590,11 +649,11 @@ const ObservationPage = () => {
{showPlaylist && ( -
+

Estúdio 10.000+

-

Configure seus playbacks e brilhe

+

Configuração Avançada de Playbacks

@@ -603,7 +662,7 @@ const ObservationPage = () => {
- setSearchQuery(e.target.value)} className="w-full bg-white/5 border-2 border-white/10 rounded-2xl py-5 pl-14 pr-6 text-white font-bold focus:outline-none focus:border-[#E3B341] transition-all placeholder:text-white/20 uppercase text-sm" /> + setSearchQuery(e.target.value)} className="w-full bg-white/5 border-2 border-white/10 rounded-2xl py-5 pl-14 pr-6 text-white font-bold focus:outline-none focus:border-[#E3B341] transition-all placeholder:text-white/20 uppercase text-sm" />
@@ -616,56 +675,87 @@ const ObservationPage = () => {
+
{ + setSongToUpload({ id: 'NEW_CUSTOM' }); + fileInputRef.current?.click(); + }}> + +

Adicionar Nova Música do Dispositivo

+

CARREGUE SEUS PRÓPRIOS PLAYBACKS MP3/WAV

+
+ {filteredSongs.length > 0 ? filteredSongs.map(song => ( -
selectSong(song)}> -
- - {customAudioMap[song.id] && } - {song.title} - -
- {song.artist} - - {song.genre} -
-
-
- -
- -
-
+
+
selectSong(song)}> +
+ + {customAudioMap[song.id] && } + {song.title} + +
+ {song.artist} + + {song.genre} +
+
+ +
+ +
+ +
+
+
+ + {editingSongId === song.id && ( +
+ + + {customAudioMap[song.id] && !song.isUserAdded && ( + + )} + + {song.isUserAdded && ( + + )} + + +
+ )}
)) : (

Nenhuma música encontrada

-

TENTE OUTROS TERMOS OU CATEGORIAS

)} - -
{ - setSongToUpload({ id: 'new-custom', title: 'Upload Personalizado', artist: 'Arquivo Local', genre: 'Personalizado' }); - fileInputRef.current?.click(); - }}> - -

Carregar Áudio Personalizado

-

Substitua playbacks por seus próprios arquivos MP3/WAV

-
- Configuração de Playback - LOCAL & CLOUD SYNC ATIVO + Estúdio Studio Master 1.0 + PLAYBACKS: {fullSongDatabase.length} DISPONÍVEIS
-
- -
-
-
+
+ {Object.keys(customAudioMap).length > 0 && ( + + {Object.keys(customAudioMap).length} ÁUDIOS PERSONALIZADOS + + )}