diff --git a/src/components/Group.tsx b/src/components/Group.tsx
index 3831e3a..62b4246 100644
--- a/src/components/Group.tsx
+++ b/src/components/Group.tsx
@@ -19,6 +19,7 @@ type GroupProps = {
group: Snapcast.Group;
snapcontrol: SnapControl;
showOffline: boolean;
+ autoPlay: boolean;
};
type GroupVolumeChange = {
diff --git a/src/components/Server.tsx b/src/components/Server.tsx
index d1ce61d..543457b 100644
--- a/src/components/Server.tsx
+++ b/src/components/Server.tsx
@@ -7,13 +7,14 @@ type ServerProps = {
server: Snapcast.Server;
snapcontrol: SnapControl;
showOffline: boolean;
+ autoPlay: boolean;
};
export default function Server(props: ServerProps) {
console.log("Render Server");
return (
- {props.server.groups.map(group => )}
+ {props.server.groups.map(group => )}
);
}
diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx
index 305644d..7f1c036 100644
--- a/src/components/Settings.tsx
+++ b/src/components/Settings.tsx
@@ -6,12 +6,14 @@ export default function SettingsDialog(props: { open: boolean, onClose: (_apply:
const [serverurl, setServerurl] = useState(config.baseUrl);
const [theme, setTheme] = useState(config.theme);
const [showOffline, setShowOffline] = useState(config.showOffline);
+ const [autoPlay, setAutoPlay] = useState(config.autoPlay);
function handleClose(apply: boolean) {
if (apply) {
config.baseUrl = serverurl;
config.theme = theme;
config.showOffline = showOffline;
+ config.autoPlay = autoPlay;
}
props.onClose(apply);
}
@@ -43,6 +45,7 @@ export default function SettingsDialog(props: { open: boolean, onClose: (_apply:
, checked: boolean) => setShowOffline(checked)} />} label="Show offline clients" />
+ , checked: boolean) => setAutoPlay(checked)} />} label="Autoplay on connect" />
diff --git a/src/components/SnapWeb.tsx b/src/components/SnapWeb.tsx
index 73a302e..77b22d3 100644
--- a/src/components/SnapWeb.tsx
+++ b/src/components/SnapWeb.tsx
@@ -93,6 +93,8 @@ export default function SnapWeb() {
const [server, setServer] = useState(new Snapcast.Server());
const [drawerOpen, setDrawerOpen] = useState(false);
const [showOffline, setShowOffline] = useState(config.showOffline);
+ const [autoPlay, setAutoPlay] = useState(config.autoPlay);
+ const [autoplaySuccess, setAutoplaySuccess] = useState(true);
const [theme, setTheme] = useState(config.theme);
const [serverUrl, setServerUrl] = useState(config.baseUrl);
const [aboutOpen, setAboutOpen] = useState(false);
@@ -134,6 +136,25 @@ export default function SnapWeb() {
updateMediaSession();
}
+ function shouldAutoplay(): boolean {
+ return (autoPlay || (document.location.hash.match(/autoplay/) !== null));
+ }
+
+ function isAutoplaySupported(): string {
+ if ("getAutoplayPolicy" in navigator) {
+ try {
+ const policy = (navigator as any).getAutoplayPolicy("mediaelement");
+ return policy;
+ } catch (error) {
+ console.warn("getAutoplayPolicy failed:", error);
+ }
+ }
+
+ return "unknown"
+ };
+
+ const autoplayPolicy = isAutoplaySupported()
+
snapControlRef.current.onChange = (_control: SnapControl, server: Snapcast.Server) => handleChange(server);
snapControlRef.current.onConnectionChanged = (_control: SnapControl, connected: boolean, error?: string) => {
console.log("Connection state changed: " + connected + ", error: " + error);
@@ -144,6 +165,18 @@ export default function SnapWeb() {
setConnectError(error);
}
setConnected(connected);
+ if (shouldAutoplay()) {
+ if (autoplayPolicy === "allowed") {
+ console.debug("autoplayPolicy:", autoplayPolicy)
+ setIsPlaying(true);
+ } else if (autoplayPolicy === "unknown") {
+ console.warn("autoplayPolicy unknown, attempting autoplay anyway")
+ setIsPlaying(true);
+ } else {
+ console.warn("autoplayPolicy:", autoplayPolicy)
+ setAutoplaySuccess(false)
+ }
+ }
};
@@ -277,9 +310,22 @@ export default function SnapWeb() {
console.debug("isPlaying changed to true");
audioRef.current.src = silence;
audioRef.current.loop = true;
- audioRef.current.play().then(() => {
- snapstreamRef.current = new SnapStream(config.baseUrl);
- });
+ audioRef.current.play().then(
+ () => {
+ setAutoplaySuccess(true)
+ snapstreamRef.current = new SnapStream(config.baseUrl);
+ },
+ (error) => {
+ setAutoplaySuccess(false)
+ if (snapstreamRef.current)
+ snapstreamRef.current.stop();
+ snapstreamRef.current = null;
+ audioRef.current.pause();
+ audioRef.current.src = '';
+ setIsPlaying(false)
+ console.error("Playing failed, likely due to disallowed autoplay:", error)
+ }
+ );
// updateMediaSession();
// });
} else {
@@ -322,6 +368,19 @@ export default function SnapWeb() {
function snackbar() {
if (isConnected) {
+ if (shouldAutoplay() && !autoplaySuccess) {
+ return (
+ { if (reason !== 'clickaway') { console.log("Snackbar - onClose") } }}>
+ { console.log("Snackbar - alert onClose") }} severity="error" sx={{ width: '100%' }}>
+ {"Autoplay failed with policy: " + autoplayPolicy}
+
+
+ )
+ }
return (null);
}
return (
@@ -370,7 +429,7 @@ export default function SnapWeb() {
sx={{ mr: 2 }}
onClick={(_) => { setIsPlaying(!isPlaying); }}
>
- {isPlaying ? : }
+ {isPlaying && autoplaySuccess ? : }
: }
@@ -381,7 +440,7 @@ export default function SnapWeb() {
>
{list()}
-
+
{snackbar()}
{ setAboutOpen(false); }} />
{
@@ -391,6 +450,7 @@ export default function SnapWeb() {
setServerUrl(config.baseUrl);
setTheme(config.theme);
setShowOffline(config.showOffline);
+ setAutoPlay(config.autoPlay);
}
}} />
diff --git a/src/config.ts b/src/config.ts
index f2ac686..8ac52e8 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -3,7 +3,8 @@ const host = import.meta.env.VITE_APP_SNAPSERVER_HOST || window.location.host;
const keys = {
snapserver_host: "snapserver.host",
theme: "theme",
- showoffline: "showoffline"
+ showoffline: "showoffline",
+ autoPlay: "autoPlay"
}
enum Theme {
@@ -48,6 +49,12 @@ const config = {
},
set showOffline(value: boolean) {
setPersistentValue(keys.showoffline, String(value));
+ },
+ get autoPlay() {
+ return getPersistentValue(keys.autoPlay, String(false)) === String(true);
+ },
+ set autoPlay(value: boolean) {
+ setPersistentValue(keys.autoPlay, String(value));
}
};