various changes

This commit is contained in:
2025-08-09 07:10:04 -04:00
parent fbd342c6a7
commit 21796e768e
20 changed files with 886 additions and 342 deletions

View File

@@ -1,11 +1,11 @@
import { useState, useEffect, useRef } from "react";
import Hls from "hls.js";
import React, { useState, useEffect, useRef, Suspense, lazy } from "react";
import { metaData } from "../config";
import Play from "@mui/icons-material/PlayArrow";
import Pause from "@mui/icons-material/Pause";
import "@styles/player.css";
const API_URL = "https://api.codey.lol";
import { API_URL } from "@/config";
const STATIONS = {
main: { label: "Main" },
rock: { label: "Rock" },
@@ -15,7 +15,7 @@ const STATIONS = {
pop: { label: "Pop" },
};
export function Player() {
export default function Player() {
const [activeStation, setActiveStation] = useState("main");
const [isPlaying, setIsPlaying] = useState(false);
const [trackTitle, setTrackTitle] = useState("");
@@ -40,60 +40,72 @@ export function Player() {
return `${mins}:${secs}`;
};
// Set page title based on current track and station
const setPageTitle = (artist, song) => {
document.title = `${metaData.title} - Radio - ${artist} - ${song} [${activeStation}]`;
};
// Initialize or switch HLS stream
const initializeStream = (station) => {
const audio = audioElement.current;
if (!audio) return;
const streamUrl = `https://stream.codey.lol/hls/${station}/${station}.m3u8`;
import('hls.js').then(({ default: Hls }) => {
const audio = audioElement.current;
if (!audio) return;
const streamUrl = `https://stream.codey.lol/hls/${station}/${station}.m3u8`;
// Clean up previous HLS
if (hlsInstance.current) {
hlsInstance.current.destroy();
hlsInstance.current = null;
}
audio.pause();
audio.removeAttribute("src");
audio.load();
// Handle audio load errors
audio.onerror = () => {
setTrackTitle("Offline");
setIsPlaying(false);
};
if (audio.canPlayType("application/vnd.apple.mpegurl")) {
audio.src = streamUrl;
audio.load();
audio.play().then(() => setIsPlaying(true)).catch(() => {
setTrackTitle("Offline");
setIsPlaying(false);
});
return;
}
if (!Hls.isSupported()) {
console.error("HLS not supported");
return;
}
const hls = new Hls({ lowLatencyMode: true, abrEnabled: false });
hlsInstance.current = hls;
hls.attachMedia(audio);
hls.on(Hls.Events.MEDIA_ATTACHED, () => hls.loadSource(streamUrl));
hls.on(Hls.Events.MANIFEST_PARSED, () => {
audio.play().then(() => setIsPlaying(true)).catch(() => {
setTrackTitle("Offline");
setIsPlaying(false);
});
});
hls.on(Hls.Events.ERROR, (event, data) => {
console.warn("HLS error:", data);
if (data.fatal) {
hls.destroy();
// Clean up previous HLS
if (hlsInstance.current) {
hlsInstance.current.destroy();
hlsInstance.current = null;
setTrackTitle("Offline");
setIsPlaying(false);
}
audio.pause();
audio.removeAttribute("src");
audio.load();
// Handle audio load errors
audio.onerror = () => {
setIsPlaying(false);
};
if (audio.canPlayType("application/vnd.apple.mpegurl")) {
audio.src = streamUrl;
audio.load();
audio.play().then(() => setIsPlaying(true)).catch(() => {
setTrackTitle("Offline");
setIsPlaying(false);
});
return;
}
if (!Hls.isSupported()) {
console.error("HLS not supported");
return;
}
const hls = new Hls({
lowLatencyMode: true,
abrEnabled: false,
liveSyncDuration: 3, // seconds behind live edge target
liveMaxLatencyDuration: 10, // max allowed latency before catchup
liveCatchUpPlaybackRate: 1.05, // playback speed when catching up
});
hlsInstance.current = hls;
hls.attachMedia(audio);
hls.on(Hls.Events.MEDIA_ATTACHED, () => hls.loadSource(streamUrl));
hls.on(Hls.Events.MANIFEST_PARSED, () => {
audio.play().then(() => setIsPlaying(true)).catch(() => {
setIsPlaying(false);
});
});
hls.on(Hls.Events.ERROR, (event, data) => {
console.warn("HLS error:", data);
if (data.fatal) {
hls.destroy();
hlsInstance.current = null;
setTrackTitle("Offline");
setIsPlaying(false);
}
});
});
};
@@ -192,6 +204,7 @@ export function Player() {
setLyrics([]);
setCurrentLyricIndex(0);
setPageTitle(trackData.artist, trackData.song);
// Fetch lyrics as before
const lyricsResponse = await fetch(`${API_URL}/lyric/search`, {
@@ -231,6 +244,16 @@ export function Player() {
return () => clearInterval(metadataInterval);
}, [activeStation]);
const progress = (elapsedTime / trackDuration) * 100;
const remaining = trackDuration - elapsedTime;
const progressColorClass =
progress >= 90
? "bg-red-500 dark:bg-red-400"
: progress >= 75 || remaining <= 20
? "bg-yellow-400 dark:bg-yellow-300"
: "bg-blue-500 dark:bg-blue-400";
return (
<>
@@ -267,12 +290,13 @@ export function Player() {
<p className="music-time__current">{formatTime(elapsedTime)}</p>
<p className="music-time__last">{formatTime(trackDuration - elapsedTime)}</p>
</div>
<div className="progress-bar-container w-full h-2 rounded bg-neutral-300 overflow-hidden">
<div className="progress-bar-container w-full h-2 rounded bg-neutral-300 dark:bg-neutral-700 overflow-hidden">
<div
className="progress-bar-fill h-full transition-all duration-200 bg-blue-500"
style={{ width: `${(elapsedTime / trackDuration) * 100}%` }}
/>
className={`h-full transition-all duration-200 ${progressColorClass}`}
style={{ width: `${progress}%` }}
></div>
</div>
<div className={`lrc-text ${lyrics.length === 0 ? 'empty' : ''}`}>
{lyrics.map((lyricObj, index) => (
<p