This commit is contained in:
2025-08-14 11:40:02 -04:00
parent ccea5db9e9
commit dbb70fc743
7 changed files with 112 additions and 38 deletions

View File

@@ -14,7 +14,6 @@ const MediaRequestForm = lazy(() => import('./TRip/MediaRequestForm.jsx'));
const RequestManagement = lazy(() => import('./TRip/RequestManagement.jsx'));
const Player = lazy(() => import('./AudioPlayer.jsx'));
export default function Root({ child }) {
window.toast = toast;
const theme = document.documentElement.getAttribute("data-theme")

View File

@@ -11,7 +11,6 @@ const STATIONS = {
rock: { label: "Rock" },
rap: { label: "Rap" },
electronic: { label: "Electronic" },
classical: { label: "Classical" },
pop: { label: "Pop" },
};
@@ -84,8 +83,8 @@ export default function Player() {
const hls = new Hls({
lowLatencyMode: true,
abrEnabled: false,
liveSyncDuration: 2.5, // seconds behind live edge target
liveMaxLatencyDuration: 3.5, // max allowed latency before catchup
liveSyncDuration: 1.0, // seconds behind live edge target
liveMaxLatencyDuration: 2.0, // max allowed latency before catchup
liveCatchUpPlaybackRate: 1.05, // playback speed when catching up
});
@@ -111,8 +110,6 @@ export default function Player() {
// Update elapsed time smoothly
useEffect(() => {
if (!isPlaying) return;
const intervalId = setInterval(() => {
const now = Date.now();
const deltaSec = (now - lastUpdateTimestamp.current) / 1000;

View File

@@ -1,10 +1,10 @@
---
import { metaData, API_URL } from "../config";
import { metaData, ENVIRONMENT } from "../config";
import RandomMsg from "../components/RandomMsg";
import { buildTime, buildNumber } from '../utils/buildTime.js';
const YEAR = new Date().getFullYear();
var ENVIRONMENT = (Astro.url.hostname === "localhost") ? "Dev": "Prod";
---

View File

@@ -4,7 +4,7 @@ import { Button } from "@mui/joy";
import { Accordion, AccordionTab } from "primereact/accordion";
import { AutoComplete } from "primereact/autocomplete";
import BreadcrumbNav from "./BreadcrumbNav";
import { API_URL } from "@/config";
import { API_URL, ENVIRONMENT } from "@/config";
export default function MediaRequestForm() {
const [type, setType] = useState("artist");
@@ -25,6 +25,8 @@ export default function MediaRequestForm() {
const debounceTimeout = useRef(null);
const autoCompleteRef = useRef(null);
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); // Helper for delays
// Helper fetch wrapper that includes cookies automatically
const authFetch = async (url, options = {}) => {
const opts = {
@@ -55,10 +57,17 @@ export default function MediaRequestForm() {
debounceTimeout.current = setTimeout(async () => {
try {
// Ensure at least 600ms between actual requests
const now = Date.now();
if (!searchArtists.lastCall) searchArtists.lastCall = 0;
const elapsed = now - searchArtists.lastCall;
const minDelay = 600; // ms
if (elapsed < minDelay) await delay(minDelay - elapsed);
searchArtists.lastCall = Date.now();
const res = await authFetch(
`${API_URL}/trip/get_artists_by_name?artist=${encodeURIComponent(
query
)}`,
`${API_URL}/trip/get_artists_by_name?artist=${encodeURIComponent(query)}`
);
if (!res.ok) throw new Error("API error");
const data = await res.json();
@@ -67,9 +76,11 @@ export default function MediaRequestForm() {
toast.error("Failed to fetch artist suggestions.");
setArtistSuggestions([]);
}
}, 300);
}, 500); // debounce 500ms
};
//helpers for string truncation
const truncate = (text, maxLen) =>
maxLen <= 3
@@ -205,18 +216,23 @@ export default function MediaRequestForm() {
if (type !== "artist" || albums.length === 0) return;
let isCancelled = false;
const albumsToFetch = albums.filter((a) => !tracksByAlbum[a.id]);
if (albumsToFetch.length === 0) return;
const fetchTracksSequentially = async () => {
const minDelay = 600; // ms between API requests
for (const album of albumsToFetch) {
if (isCancelled) break;
setLoadingAlbumId(album.id);
try {
const now = Date.now();
if (!fetchTracksSequentially.lastCall) fetchTracksSequentially.lastCall = 0;
const elapsed = now - fetchTracksSequentially.lastCall;
if (elapsed < minDelay) await delay(minDelay - elapsed);
fetchTracksSequentially.lastCall = Date.now();
const res = await authFetch(`${API_URL}/trip/get_tracks_by_album_id/${album.id}`);
if (!res.ok) throw new Error("API error");
const data = await res.json();
@@ -299,7 +315,12 @@ export default function MediaRequestForm() {
try {
// Example: simulate submission delay
await new Promise((resolve) => setTimeout(resolve, 1500));
toast.success("Request submitted!");
const allSelectedIds = Object.values(selectedTracks)
.filter(arr => Array.isArray(arr)) // skip null entries
.flat();
toast.success(`Request submitted! (${allSelectedIds.length} tracks)`);
console.debug("Requested: ", selectedTracks);
console.debug("Flattened: ", allSelectedIds);
} catch (err) {
toast.error("Failed to submit request.");
} finally {
@@ -310,6 +331,7 @@ export default function MediaRequestForm() {
return (
<div className="max-w-3xl mx-auto my-10 p-6 rounded-xl shadow-md bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-100 border border-neutral-200 dark:border-neutral-700">
<style>{`
/* Accordion tab backgrounds & text */
.p-accordion-tab {
background-color: #ffffff;
color: #000000;
@@ -318,14 +340,65 @@ export default function MediaRequestForm() {
background-color: #1e1e1e;
color: #ffffff;
}
/* Accordion header link */
.p-accordion-header .p-accordion-header-link {
background-color: #f9f9f9;
color: #000000;
}
[data-theme="dark"] .p-accordion-header .p-accordion-header-link {
background-color: #1e1e1e !important;
color: #ffffff !important;
}
/* Accordion content panel */
.p-accordion-content {
background-color: #fafafa;
color: #000000;
}
[data-theme="dark"] .p-accordion-content {
background-color: #2a2a2a;
color: #ffffff;
}
/* Track list UL/LI styling */
.p-accordion-content ul {
padding-left: 0;
margin: 0;
list-style: none;
}
[data-theme="dark"] .p-accordion-content ul {
color: #ffffff;
}
/* Track items */
.p-accordion-content li {
background-color: #fff;
border-bottom: 1px solid #e5e5e5;
}
[data-theme="dark"] .p-accordion-content li {
background-color: #2a2a2a;
border-bottom: 1px solid #444;
}
/* Checkboxes inside track list */
.p-accordion-content input[type="checkbox"] {
accent-color: #1d4ed8; /* optional for consistent dark mode styling */
}
/* Loading spinner (optional darker style) */
[data-theme="dark"] .animate-spin {
border-color: #555;
border-top-color: #1d4ed8;
}
/* Small text like audio quality, duration, version */
.p-accordion-content span {
color: #555;
}
[data-theme="dark"] .p-accordion-content span {
color: #aaa;
}
`}</style>
<BreadcrumbNav currentPage="request" />
@@ -369,6 +442,7 @@ export default function MediaRequestForm() {
field="artist"
completeMethod={searchArtists}
onChange={handleArtistChange}
minLength={3}
placeholder="Artist"
dropdown
className="w-full"
@@ -454,13 +528,13 @@ export default function MediaRequestForm() {
checked={selected?.includes(String(track.id))}
onChange={() => toggleTrack(id, track.id)}
className="cursor-pointer"
aria-label={`Select track ${track.title}`}
aria-label={`Select track ${track.title} `}
/>
<button
type="button"
onClick={() => handleTrackClick(track.id, selectedArtist.artist, track.title)}
className="font-medium text-blue-600 hover:underline cursor-pointer bg-transparent border-none p-0"
aria-label={`Download track ${track.title}`}
aria-label={`Download track ${track.title} `}
>
{track.title}
</button>

View File

@@ -14,3 +14,4 @@ export const socialLinks = {
export const MAJOR_VERSION = "0.0"
export const RELEASE_FLAG = "Alpha";
export const ENVIRONMENT = import.meta.env.DEV ? "Dev" : "Prod";

View File

@@ -1,8 +1,9 @@
---
import MediaRequestForm from "@/components/qs2/MediaRequestForm"
import MediaRequestForm from "@/components/TRip/MediaRequestForm"
import Base from "@/layouts/Base.astro";
import Root from "@/components/AppLayout.jsx";
import { verifyToken } from "@/utils/jwt";
import { ENVIRONMENT } from "@/config";
const token = Astro.cookies.get("access_token")?.value;
let user = null;

View File

@@ -3,14 +3,16 @@ import MediaRequestForm from "@/components/TRip/MediaRequestForm"
import Base from "@/layouts/Base.astro";
import Root from "@/components/AppLayout.jsx";
import { verifyToken } from "@/utils/jwt";
import { ENVIRONMENT } from "@/config";
const token = Astro.cookies.get("access_token")?.value;
let user = null;
try {
const IS_DEV = (ENVIRONMENT==="Dev");
if (token) {
user = verifyToken(token);
if (user) {
if (IS_DEV) {
console.log("Verified!", user);
} else {
throw Error("Authentication required");