AI refactorized code

Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-05-29 18:39:16 +02:00
padre 6c96c01f04
commit c7d2566f19
Se han modificado 8 ficheros con 190 adiciones y 207 borrados

Ver fichero

@@ -1,13 +1,7 @@
import { use, useCallback, useEffect, useRef, useState } from "react";
import WaveForm from "./components/WaveForm/WaveForm";
import { Button, Icon, Range } from "react-materialize";
import { useEffect, useState } from "react";
import style from "./App.module.css";
import { Marquee } from "./components/Marquee/Marquee";
import { Player } from "./components/Player/Player";
import { Stream } from "./components/Stream/Stream";
import useWallpaperList from "./hooks/useWallpaperList";
import Radio from "./icons/Radio.svg";
function getRandomInt(min, max) {
@@ -18,28 +12,30 @@ function getRandomInt(min, max) {
const App = () => {
const wallpapers = useWallpaperList();
const [index, setIndex] = useState(0);
const [nextIndex, setNextIndex] = useState(0);
// Preload and transition to next wallpaper
useEffect(() => {
let img = new Image();
if (!wallpapers.length) return;
const img = new window.Image();
img.src = `/wallpapers/${wallpapers[nextIndex]}`;
img.onload = () => {
setIndex(nextIndex);
setTimeout(() => {
setNextIndex(getRandomInt(0, wallpapers.length - 1));
}, getRandomInt(20000, 50000));
};
}, [nextIndex]);
}, [nextIndex, wallpapers]);
// Initialize nextIndex when wallpapers are loaded
useEffect(() => {
if (wallpapers.length === 0) return;
if (!wallpapers.length) return;
setNextIndex(getRandomInt(0, wallpapers.length - 1));
}, [wallpapers]);
const current = `/wallpapers/${wallpapers[index]}`;
const currentWallpaper = `/wallpapers/${wallpapers[index]}`;
return (
<div
@@ -49,7 +45,7 @@ const App = () => {
height: "100%",
backgroundSize: "cover",
backgroundPosition: "center",
backgroundImage: `url(${current})`,
backgroundImage: `url(${currentWallpaper})`,
transition: "background-image 1s ease-in-out",
}}
>
@@ -79,4 +75,4 @@ const App = () => {
);
};
export default App;
export default App;

Ver fichero

@@ -5,9 +5,8 @@ import Pause from "../../icons/Pause.svg";
import Volume from "../../icons/Volume.svg";
import VolumeX from "../../icons/VolumeX.svg";
import UsersIcon from "../../icons/Users.svg";
import React, { useEffect, useRef, useState } from "react";
import React, { useRef, useState } from "react";
import { VolumeSlider } from "../VolumeSlider/VolumeSlider";
import WaveForm from "../WaveForm/WaveForm";
export const Player = ({
src,
@@ -30,22 +29,21 @@ export const Player = ({
const togglePlay = () => {
if (onTogglePlay) onTogglePlay();
if (paused) {
audio.current.play();
} else {
audio.current.pause();
}
setPaused(!paused);
setPaused((prev) => !prev);
};
const toggleMute = () => {
if (muted) {
audio.current.volume = 1; // Unmute
audio.current.volume = 1;
} else {
audio.current.volume = 0; // Mute
audio.current.volume = 0;
}
setMuted(!muted);
setMuted((prev) => !prev);
};
return (
@@ -72,12 +70,12 @@ export const Player = ({
<img
tabIndex={-1}
src={paused ? Play : Pause}
alt="Play"
alt={paused ? "Play" : "Pause"}
style={paused ? { marginLeft: "6px" } : { marginLeft: "1px" }}
/>
</button>
<button onClick={toggleMute} className={style.mute}>
<img tabIndex={-1} src={muted ? VolumeX : Volume} alt="Mute" />
<img tabIndex={-1} src={muted ? VolumeX : Volume} alt={muted ? "Unmute" : "Mute"} />
</button>
<VolumeSlider muted={muted} audioElmRef={audio} setMute={setMuted} />
<span className={style.users} title="Users">
@@ -91,4 +89,4 @@ export const Player = ({
</div>
</div>
);
};
};

Ver fichero

@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useRef, useState } from "react";
import { Player } from "../Player/Player";
import { useListenerCount } from "../../hooks/useListenerCount";
import { useAudioInfo } from "../../hooks/useAudioInfo";
@@ -7,7 +7,6 @@ import WaveForm from "../WaveForm/WaveForm";
export function Stream() {
const { count, peak } = useListenerCount(10000);
const audioInfo = useAudioInfo();
const [analyzerData, setAnalyzerData] = useState(null);
const audio = useRef(null);
@@ -28,26 +27,25 @@ export function Stream() {
source.connect(analyzer);
source.connect(audioCtx.destination);
source.onended = () => {
source.disconnect();
};
source.onended = () => source.disconnect();
setAnalyzerData({ analyzer, bufferLength, dataArray });
}
};
const marqueeText = `Now Playing: ${Math.floor(audioInfo.duration)}s - ${audioInfo.title} by ${audioInfo.performer} - ${audioInfo.album} (${audioInfo.date})`;
return (
<>
{analyzerData && <WaveForm analyzerData={analyzerData} />}
<Player
src="/stream.mp3"
onTogglePlay={onTogglePlay}
marquee={`Now Playing: ${Math.floor(audioInfo.duration)}s - ${audioInfo.title
} by ${audioInfo.performer} - ${audioInfo.album} (${audioInfo.date})`}
marquee={marqueeText}
marqueeUrl={audioInfo.url}
usersCount={`${count}/${peak}`}
ref={setAudioRef}
/>
</>
);
}
}

Ver fichero

@@ -2,31 +2,29 @@ import { useState } from "react";
import style from "./VolumeSlider.module.css";
export function VolumeSlider({ muted, setMute, audioElmRef }) {
const [currentVolume, setCurrentVolume] = useState(100);
const [currentVolume, setCurrentVolume] = useState(100);
const handleVolumeChange = (event) => {
const newVolume = event.target.value;
setCurrentVolume(newVolume);
const handleVolumeChange = (event) => {
const newVolume = Number(event.target.value);
setCurrentVolume(newVolume);
audioElmRef.current.volume = newVolume / 100;
if (audioElmRef.current) {
audioElmRef.current.volume = newVolume / 100;
}
if (newVolume === "0") {
setMute(true);
} else {
setMute(false);
}
};
setMute(newVolume === 0);
};
return (
<input
className={style.volumeSlider}
style={{ backgroundSize: `${muted ? "0" : currentVolume}% 100%` }}
type="range"
min="0"
max="100"
step="1"
value={muted ? "0" : currentVolume}
onChange={handleVolumeChange}
/>
);
return (
<input
className={style.volumeSlider}
style={{ backgroundSize: `${muted ? "0" : currentVolume}% 100%` }}
type="range"
min="0"
max="100"
step="1"
value={muted ? 0 : currentVolume}
onChange={handleVolumeChange}
/>
);
}

Ver fichero

@@ -1,24 +1,23 @@
import { useRef, useEffect } from "react";
import useSize from "../../hooks/useSize";
const animateBars = (analyser, canvas, canvasCtx, dataArray, bufferLength) => {
const BLUE_SHADES = [
"rgba(255,255,255,0.5)",
"rgba(255,255,255,0.4)",
"rgba(255,255,255,0.3)",
"rgba(255,255,255,0.2)",
];
const animateBars = (analyser, canvas, ctx, dataArray, bufferLength) => {
analyser.getByteFrequencyData(dataArray);
canvasCtx.fillStyle = "#000";
const HEIGHT = canvas.height / 2;
var barWidth = Math.ceil(canvas.width / bufferLength) * 2.5;
let barHeight;
const barWidth = Math.ceil(canvas.width / bufferLength) * 2.5;
let x = 0;
for (var i = 0; i < bufferLength; i++) {
barHeight = (dataArray[i] / 255) * HEIGHT;
const blueShade = Math.floor((dataArray[i] / 255) * 4); // generate a shade of blue based on the audio input
const blueHex = [
"rgba(255,255,255,0.5)",
"rgba(255,255,255,0.4)",
"rgba(255,255,255,0.3)",
"rgba(255,255,255,0.2)",
][blueShade]; // use react logo blue shades
canvasCtx.fillStyle = blueHex;
canvasCtx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
for (let i = 0; i < bufferLength; i++) {
const barHeight = (dataArray[i] / 255) * HEIGHT;
const blueShade = Math.floor((dataArray[i] / 255) * 4);
ctx.fillStyle = BLUE_SHADES[blueShade];
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 1;
}
};
@@ -28,36 +27,36 @@ const WaveForm = ({ analyzerData }) => {
const { dataArray, analyzer, bufferLength } = analyzerData;
const [width, height] = useSize();
const draw = (dataArray, analyzer, bufferLength) => {
const canvas = canvasRef.current;
if (!canvas || !analyzer) return;
const canvasCtx = canvas.getContext("2d");
const animate = () => {
requestAnimationFrame(animate);
// eslint-disable-next-line no-self-assign
canvas.width = canvas.width;
canvasCtx.translate(0, canvas.offsetHeight / 2);
animateBars(analyzer, canvas, canvasCtx, dataArray, bufferLength);
};
animate();
};
useEffect(() => {
draw(dataArray, analyzer, bufferLength);
}, [dataArray, analyzer, bufferLength]);
const canvas = canvasRef.current;
if (!canvas || !analyzer) return;
const ctx = canvas.getContext("2d");
let animationId;
const render = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(0, canvas.height / 2);
animateBars(analyzer, canvas, ctx, dataArray, bufferLength);
ctx.restore();
animationId = requestAnimationFrame(render);
};
render();
return () => {
cancelAnimationFrame(animationId);
};
}, [dataArray, analyzer, bufferLength, width, height]);
return (
<canvas
style={{
position: "absolute",
top: "0",
left: "0",
zIndex: "0",
top: 0,
left: 0,
zIndex: 0,
}}
ref={canvasRef}
width={width}
@@ -66,4 +65,4 @@ const WaveForm = ({ analyzerData }) => {
);
};
export default WaveForm;
export default WaveForm;

Ver fichero

@@ -1,65 +1,52 @@
import { useEffect, useState } from "react";
export function useAudioInfo() {
const [json, setJson] = useState(null);
const [info, setInfo] = useState({
title: "",
performer: "",
album: "",
url: "",
duration: 0,
date: "",
});
const [info, setInfo] = useState({
title: "",
performer: "",
album: "",
url: "",
duration: 0,
date: "",
});
useEffect(() => {
let isMounted = true;
useEffect(() => {
let isMounted = true;
const fetchAudioInfo = async () => {
try {
const response = await fetch("/stream.json");
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const json = await response.json();
if (!isMounted || !json) return;
let url = json?.media?.['@ref'];
const url = json?.media?.["@ref"] || "";
const data = json.media?.track?.[0];
if (json.media?.track && json.media.track[0]) {
const data = json.media.track[0];
setInfo({
title: data.Title || "",
performer: data.Performer || "",
album: data.Album || "",
url: url || "",
duration: data.Duration || 0,
date: data.Recorded_Date || "",
});
if (data) {
setInfo({
title: data.Title || "",
performer: data.Performer || "",
album: data.Album || "",
url,
duration: data.Duration || 0,
date: data.Recorded_Date || "",
});
}
} catch (error) {
console.error("Error fetching audio info:", error);
}
};
return () => {
isMounted = false;
};
}, [json]);
fetchAudioInfo();
const intervalId = setInterval(fetchAudioInfo, 30000);
useEffect(() => {
const fetchAudioInfo = async () => {
try {
const response = await fetch('/stream.json');
return () => {
isMounted = false;
clearInterval(intervalId);
};
}, []);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setJson(data);
}
catch (error) {
console.error('Error fetching audio info:', error);
return;
}
};
fetchAudioInfo();
const intervalId = setInterval(fetchAudioInfo, 30000); // Refresh every 10 seconds
return () => clearInterval(intervalId);
}, []);
return info;
return info;
}

Ver fichero

@@ -1,50 +1,52 @@
import { useEffect, useState } from "react";
export function useListenerCount(refreshInterval = 10000) {
const [count, setCount] = useState(0);
const [peak, setPeak] = useState(0);
const [count, setCount] = useState(0);
const [peak, setPeak] = useState(0);
useEffect(() => {
let isMounted = true;
useEffect(() => {
let isMounted = true;
async function fetchListenerCount() {
try {
const response = await fetch('/status.xsl');
const data = await response.text();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(data, 'text/html');
const listeners = xmlDoc.getElementsByTagName('td');
const fetchListenerCount = async () => {
try {
const response = await fetch("/status.xsl");
const data = await response.text();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(data, "text/html");
const listeners = xmlDoc.getElementsByTagName("td");
for (let i = 0; i < listeners.length; i++) {
if (i === 9) {
if (isMounted) {
setCount(listeners[i].textContent);
}
}
else if (i === 11) {
const currentPeak = parseInt(listeners[i].textContent, 10);
if (isMounted && currentPeak > peak) {
setPeak(currentPeak);
}
}
}
} catch (error) {
console.error('Error fetching listener count:', error);
}
let newCount = count;
let newPeak = peak;
for (let i = 0; i < listeners.length; i++) {
if (i === 9) {
newCount = parseInt(listeners[i].textContent, 10);
} else if (i === 11) {
newPeak = parseInt(listeners[i].textContent, 10);
}
}
fetchListenerCount();
const intervalId = setInterval(fetchListenerCount, refreshInterval);
return () => {
isMounted = false;
clearInterval(intervalId);
};
}, []);
return {
count: parseInt(count, 10),
peak: parseInt(peak, 10)
if (isMounted) {
setCount(newCount);
setPeak((prevPeak) => (newPeak > prevPeak ? newPeak : prevPeak));
}
} catch (error) {
console.error("Error fetching listener count:", error);
}
};
fetchListenerCount();
const intervalId = setInterval(fetchListenerCount, refreshInterval);
return () => {
isMounted = false;
clearInterval(intervalId);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return {
count: parseInt(count, 10),
peak: parseInt(peak, 10),
};
}

Ver fichero

@@ -1,19 +1,24 @@
import { useState, useEffect } from "react";
export default function useWallpaperList() {
const [list, setList] = useState([]);
useEffect(() => {
fetch("/wallpapers/list.txt")
.then((res) => res.text())
.then((text) => {
const lines = text
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
setList(lines);
});
}, []);
return list;
}
import { useState, useEffect } from "react";
export default function useWallpaperList() {
const [list, setList] = useState([]);
useEffect(() => {
const fetchList = async () => {
try {
const res = await fetch("/wallpapers/list.txt");
const text = await res.text();
const lines = text
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
setList(lines);
} catch (error) {
console.error("Error fetching wallpaper list:", error);
}
};
fetchList();
}, []);
return list;
}