Adds feature flags, and the ability to do browser playback

This commit is contained in:
2025-06-24 12:50:15 -07:00
parent 480b30d909
commit 5e9842f02d
9 changed files with 1142 additions and 668 deletions

View File

@@ -7,6 +7,12 @@ export interface NowPlayingResponse {
currentFile: string;
}
export interface Features {
video: boolean;
screenshare: boolean;
browserPlayback: boolean;
}
export interface Metadata {
title?: string;
description?: string;
@@ -57,6 +63,11 @@ export enum ServerEvent {
}
export const API = {
async getFeatures(): Promise<Features> {
const response = await fetch('/api/features');
return response.json();
},
async getPlaylist(): Promise<PlaylistItem[]> {
const response = await fetch('/api/playlist');
return response.json();

View File

@@ -4,11 +4,12 @@ import NowPlaying from './NowPlaying';
import AddSongPanel from './AddSongPanel';
import RenameFavoriteModal from './RenameFavoriteModal';
import { TabView, Tab } from './TabView';
import { API, getDisplayTitle, PlaylistItem, ServerEvent } from '../api/player';
import { API, Features, getDisplayTitle, PlaylistItem, ServerEvent } from '../api/player';
import { FaMusic, FaHeart, FaPlus, FaEdit } from 'react-icons/fa';
import useWebSocket from 'react-use-websocket';
import classNames from 'classnames';
import { useScreenShare } from '../hooks/useScreenShare';
import AudioPlayer from './AudioPlayer';
enum Tabs {
Playlist = "playlist",
@@ -93,6 +94,8 @@ const App: React.FC = () => {
const [selectedTab, setSelectedTab] = useState<Tabs>(Tabs.Playlist);
const [isRenameModalOpen, setIsRenameModalOpen] = useState(false);
const [favoriteToRename, setFavoriteToRename] = useState<PlaylistItem | null>(null);
const [audioEnabled, setAudioEnabled] = useState(false);
const [features, setFeatures] = useState<Features | null>(null);
const {
isScreenSharing,
@@ -124,6 +127,9 @@ const App: React.FC = () => {
setIsPlaying(!nowPlaying.isPaused);
setVolume(nowPlaying.volume);
setIsIdle(nowPlaying.playingItem ? !nowPlaying.playingItem.playing : true);
const features = await API.getFeatures();
setFeatures(features);
}, [volumeSettingIsLocked]);
const handleAddURL = async (url: string) => {
@@ -313,6 +319,10 @@ const App: React.FC = () => {
return (
<div className="flex items-center justify-center h-screen w-screen bg-black md:py-10">
<div className="bg-violet-900 w-full md:max-w-2xl h-full md:max-h-xl md:border md:rounded-2xl flex flex-col">
{features?.browserPlayback && (
<AudioPlayer isPlaying={isPlaying} enabled={audioEnabled} />
)}
<NowPlaying
className="flex flex-row md:rounded-t-2xl"
songName={nowPlayingSong || "(Not Playing)"}
@@ -330,6 +340,9 @@ const App: React.FC = () => {
onVolumeWillChange={() => setVolumeSettingIsLocked(true)}
onVolumeDidChange={() => setVolumeSettingIsLocked(false)}
isScreenSharingSupported={isScreenSharingSupported}
features={features}
audioEnabled={audioEnabled}
onAudioEnabledChange={setAudioEnabled}
/>
<TabView selectedTab={selectedTab} onTabChange={setSelectedTab}>

View File

@@ -0,0 +1,32 @@
import React, { useEffect, useRef } from 'react';
interface AudioPlayerProps {
isPlaying: boolean;
enabled: boolean;
}
const AudioPlayer: React.FC<AudioPlayerProps> = ({ isPlaying, enabled }) => {
const audioRef = useRef<HTMLAudioElement>(null);
useEffect(() => {
if (enabled && isPlaying) {
console.log("Playing audio");
audioRef.current?.play().catch((error) => {
console.error("Audio playback error:", error);
});
} else {
console.log("Pausing audio");
audioRef.current?.pause();
}
}, [isPlaying, enabled]);
return (
<audio
ref={audioRef}
src="/stream/audio.m3u8"
preload="metadata"
/>
);
};
export default AudioPlayer;

View File

@@ -1,6 +1,7 @@
import React, { HTMLAttributes } from 'react';
import classNames from 'classnames';
import { FaPlay, FaPause, FaStepForward, FaStepBackward, FaVolumeUp, FaDesktop, FaStop } from 'react-icons/fa';
import { Features } from '../api/player';
interface NowPlayingProps extends HTMLAttributes<HTMLDivElement> {
songName: string;
@@ -25,6 +26,11 @@ interface NowPlayingProps extends HTMLAttributes<HTMLDivElement> {
// Sent when the volume has changed
onVolumeDidChange: (volume: number) => void;
features: Features | null;
audioEnabled: boolean;
onAudioEnabledChange: (enabled: boolean) => void;
}
const NowPlaying: React.FC<NowPlayingProps> = (props) => {
@@ -80,7 +86,7 @@ const NowPlaying: React.FC<NowPlayingProps> = (props) => {
<FaStepForward size={24} />
</button>
{props.isScreenSharingSupported && (
{(props.isScreenSharingSupported && props.features?.screenshare) && (
<button
className={classNames("text-white hover:text-violet-300 transition-colors rounded-full p-2", props.isScreenSharing ? ' bg-violet-800' : '')}
onClick={props.onScreenShare}
@@ -90,6 +96,20 @@ const NowPlaying: React.FC<NowPlayingProps> = (props) => {
</button>
)}
</div>
{props.features?.browserPlayback && (
<div>
<label className="flex items-center gap-2 text-white text-sm cursor-pointer">
<input
type="checkbox"
checked={props.audioEnabled}
onChange={(e) => props.onAudioEnabledChange(e.target.checked)}
className="w-4 h-4 text-violet-600 bg-gray-100 border-gray-300 rounded focus:ring-violet-500 focus:ring-2"
/>
Enable audio playback in browser
</label>
</div>
)}
</div>
</div>
</div>